类与对象<五>
转眼北京就已经进入了寒冷的冬季了,如今也是晚上最适合睡觉的季节,每天早上都有种不想上班的冲动,只是没有暖气的日子也挺煎熬的,像南方老家冬天的那种感觉,上周末第一次练车,感觉还是挺刺激的,不过有点手忙脚乱,下周还得继续,这个月的目标是将证拿到手,得继续油啦~~~好了,随笔了一下,下面开始学习。



- 在关键字public后面声明,它们是类与外部的接口,任何外部函数都可以访问公有类型数据和函数。
- 在关键字private后面声明,只允许本类中的函数访问,而类外部的任何函数都不能访问。
- 在关键字protected后面声明,与private类似,其差别表现在继承与派生时对派生类的影响不同。
对于熟悉Java的来说,对于类是太熟悉不过了,不过C++还是有它自身的特点,所以还是按部就班的学习,下面来新建一个类来体验下:





上面是自动生成的类相关的代码,对于初学者就手工去敲,其中对于头文件中有一句代码:




新建一个主函数来调用下,切到解决方案视图:



编译运行:

这时秒钟走了一秒,可以这样模拟:

因为second这个成员变量的访问修饰符是public的,所以类可以直接访问,而一般成员变量会声明成私的的,这时就需要暴露一个public的方法来对其进行更新,修改程序如下:



这样输出的结果还是一样,另外要去在外去单独去访问私有的成员变量,则需要生成成员变量的set和get方法,学过java的都很容易理解,具体代码如下:
Clock.h:
//#pragma once 它是防止头文件重复包含的,跟之前我们编写的等价 #ifndef _CLOCK_H_ #define _CLOCK_H_ class Clock { public: void display(); void init(int hour, int minute, int second); void update(); int getHour(); int getMinute(); int getSecond(); void setHour(int hour); void setMinute(int minute); void setSecond(int second); private: int hour; int minute; int second; }; #endif //_CLOCK_H_
Clock.cpp:
#include "Clock.h" #include <iostream> using namespace std; void Clock::display() { cout<<hour<<":"<<minute<<":"<<second<<endl; } void Clock::init(int hour, int minute, int second) { this->hour = hour; this->minute = minute; this->second = second; } void Clock::update() { this->second++; if(this->second == 60) { this->minute++; this->second = 0; } if(this->minute == 60) { this->hour++; this->minute = 0; this->second = 0; } } int Clock::getHour() { return this->hour; } int Clock::getMinute() { return this->minute; } int Clock::getSecond() { return this->second; } void Clock::setHour(int hour) { this->hour = hour; } void Clock::setMinute(int minute) { this->minute = minute; } void Clock::setSecond(int second) { this->second = second; }

关于它的概念在之前已经学习过,具体可以参考博文:http://www.cnblogs.com/webor2006/p/4896191.html
而类中的成员函数也可以做成内联的,下面则通过代码来认识一下:
定义内联成员函数有两种方式:
方式一:


方式二:


关于函数的重载这里就不多介绍,之前已经学过了,必须是在相同的作用域的前提下来说,而对于类的成员函数中也同样存在重载的情况,具体如下:

Test.cpp:
#include "Test.h" #include <iostream> using namespace std; //inline int Test::add(int a, int b) { // return a + b; //} void Test::init() { this->x = 0; this->y = 0; this->z = 0; } void Test::init(int x) { this->x = x; this->y = 0; this->z = 0; } void Test::init(int x, int y) { this->x = x; this->y = y; this->z = 0; } void Test::init(int x, int y, int z) { this->x = x; this->y = y; this->z = z; } void Test::display() { cout<<"x="<<this->x<<" y="<<this->y<<" z="<<this->z<<endl; }

编译运行:

实际上可以用缺省参数简化重载的代码,具体如下:


而调用的代码不用变,且结果也是一样的。另外需要注意一下可能会产生函数调用的恶意性,如下:

这时编译:

但是:



具体来看下代码:

编译运行:

实际上可以像c语言那样对struct结构体进行初始化,如下:

接着来看一下类,来跟结构体进行对比:
02.cpp:
#include <iostream> using namespace std; struct Test2 { int x; int y; int z; void init(int x, int y, int z) { this->x = x; this->y = y; this->z = z; } void display() { cout<<"x="<<this->x<<" y="<<this->y<<" z="<<this->y<<endl; } }; class Test3 { int x; int y; int z; void init(int x, int y, int z) { this->x = x; this->y = y; this->z = z; } void display() { cout<<"x="<<this->x<<" y="<<this->y<<" z="<<this->y<<endl; } }; int main(void) { /*Test2 test2; test2.init(10, 20, 30); test2.display(); Test2 test2 = {10, 20, 30}; test2.display();*/ Test3 test3; test3.init(10, 20, 30); test3.display(); return 0; }
编译运行:

那类能不能像结构体那样进行参数初始化呢?

编译运行:

那如果声明成公有的,那这种初始化方式能支持么?
class Test3 { public: int x; int y; int z; void init(int x, int y, int z) { this->x = x; this->y = y; this->z = z; } void display() { cout<<"x="<<this->x<<" y="<<this->y<<" z="<<this->y<<endl; } }; int main(void) { /*Test2 test2; test2.init(10, 20, 30); test2.display(); Test2 test2 = {10, 20, 30}; test2.display();*/ /*Test3 test3; test3.init(10, 20, 30); test3.display(); ERROR,成员是私有的,无法访问*/ Test3 test3 = {10, 20, 30}; test3.display(); return 0; }
这时编译运行:

可以看出类中的成员变量也可以像结构体那样进行初始化。

实际上对于这个关键字已经在上面用过了,java里面也有这个关键字,还是先看下介绍:
- 成员函数有一个隐含的附加形参,即指向该对象的指针,这个隐含的形参叫做this指针。
要说明白this指针,需要先从类的内存模型中来进行理解,一个类中由成员变量和成员函数构成,这里拿Test3这个类来进行说明:
所以this指针就代表对象本身。 - 使用this指针保证了每个对象可以拥有不同的数据成员,但处理这些成员的代码可以被所有对象共享。

每个类都定义了自己的作用域称为类作用域,如下面代码:

而类作用域中说明的标识符只在类中可见,也就是在类的外面则无法使用:

但是:

由作用域则引出五种作用域类别:
1、块作用域:在花括号以内的作用域

输出结果:

2、文件作用域
这个在上面有说到。
3、函数原型作用域

4、函数作用域:仅仅只是针对goto语句来说的,但goto语句不会用到,做个了解。

5、类作用域

- C++中类必须先定义,才能够实例化。
- 两个类需要相互引用形成一个“环形”引用时,无法先定义使用。这时候需要用到前向声明。
- 前向声明的类不能实例化。
新建两个类:

这里面的代码都是默认的:
A.h:
#ifndef _A_H #define _A_H class A { public: A(void); ~A(void); }; #endif
A.cpp:
#include "A.h" A::A(void) { } A::~A(void) { }
B.h:
#ifndef _B_H #define _B_H class B { public: B(void); ~B(void); }; #endif
B.cpp:
#include "B.h" B::B(void) { } B::~B(void) { }
这时A类中包含B类,代码可以这样写:

上面的写法毋庸置疑,当然是合法的。
这时,B类中也包含了A类,代码可以这样写:

这就造成A类和B类相互包含,这在语法上是不允许的,编译如下:

这时就需要用上前向声明了,在B类中进行前向声明:

编译运行:

这个错是啥意思呢?也就是上面说的第三条:前向声明的类不能实例化。也就是只支持指针或引用,修改代码如下:

编译运行:

但是对于B.cpp实现文件而言,在要使用A类时还是需要包含A.h头文件的:

或者可以用在引用中,如下:


所以需要记住:当两个类相互包含时,需要用到前向声明。

- 外围类需要使用嵌套类对象作为底层实现,并且该嵌套类只用于外围类的实现,且同时可以对用户隐藏该底层实现,具体代码如下:

编译运行:

- 从作用域的角度看,嵌套类被隐藏在外围类之中,该类名只能在外围类中使用。如果在外围类的作用域使用该类名时,需要加名字限定。

编译运行:
因为Inner对于外围类中是不可见的,需要加名字限定,如下:
其运行结果是一样的。 - 嵌套类中的成员函数可以在它的类体外定义。
这个在上面已经看到了。 - 嵌套类的成员函数对外围类的成员没有访问权,反之亦然。
也就是不能访问私有成员,修改代码如下:
编译运行:
所以就是这个道理。 - 嵌套类仅仅只是语法上的嵌入。

编译运行:
但是如果给内部类加上public关键字:
编译运行:
以上可以发现,并非嵌套类Inner只能被Outer使用,前提是被public修饰,实际上只是语法上的嵌入,在外界也可以用来使用,当然如果用private修饰Inner,则仅仅只有在Outer内部才能进行使用。

- 类也可以定义在函数体内,这样的类被称为局部类(local class)。局部类只在定义它的局部域内可见。
- 局部类的成员函数必须被定义在类体中。
- 局部类中不能有静态成员。

编译运行:
那如果在函数外使用呢?当然会报错:


这是由于静态数据成员需要在文件作用域外部才能够进行初始化,关于这点在之后会进一步学习。
浙公网安备 33010602011771号