备忘:C++ 类 (初学之实录)。
声明类的一般形式:
class 类名 [:基类1[,基类2]]
{
private :
私有的数据和成员函数;
protected
保护的数据和成员函数;
public :
公用的数据和成员函数;
};
private: 私有的。仅类内可见。子类及外部都不见。
public: 公共的。类外也可见。
protected: 受保护的。类及派生的子类可见。
使用类时注意的小问题:
使用类时,类在声明时,会直接创建类的实体。如果不想在声明时创建,一般使用指针方式声明类。如:
1
2
3
4
5
|
CTest A(x, y); // 声明时直接创建了 A类,并执行A的构造函数 CTest(x, y); CTest *B; // 先声明一个类的指针 B = new CTest(x, y); // 在需要时,再创建类的实体。 delete B; // 释放 B 的实体。如不执行delete释放。*B所指向的实体会一直存在。 // 注意: A不用释放,他会在作用域结束时,自动释放,也就是函数执行完后。会自动释放A。 |
构造函数
-
类的构造函数与类同名。
-
无参构造函数在类对象进入其作用域时调用构造函数。即类被创建时,调用构造函数。
-
构造函数没有返回值,因此也不需要在定义构造函数时声明类型,这是它和一般函数的一个重要的不同之点。
-
默认无参构造函数不需用户调用,也不能被用户调用。
-
在构造函数的函数体中不仅可以对数据成员赋初值,而且可以包含其他语句。但是一般不提倡在构造函数中加入与初始化无关的内容,以保持程序的清晰。
-
尽管在一个类中可以包含多个构造函数,但是对于每一个对象来说,建立对象时只执行其中一个构造函数,并非每个构造函数都被执行。
-
如果用户自己没有定义构造函数,则C++系统会自动生成一个构造函数,只是这个构造函数的函数体是空的,也没有参数,不执行初始化操作。
-
派生类的构造函数不会继承父类的构造函数。需要显式执行。执行方法是在派生类的构造函数后加冒号:加父类的构造函数(可带参数)
如:
1
2
3
4
5
6
7
|
class CTest1: CTest{ public : CTest1( int x, int y):CTest(x) //CTest()是 CTest1的父类的构造函数, 假设其有参数。 { ….. //语句。 }; } |
释构函数
-
类的释造函数与类同名,并在前面加上~。
-
当类对象的生命期结束时,会自动执行析构函数。
具体地说如果出现以下几种情况,程序就会执行析构函数:
-
如果在一个函数中定义了一个对象(它是自动局部对象),当这个函数被调用结束时,对象应该释放,在对象释放前自动执行析构函数。
-
static局部对象在函数调用结束时对象并不释放,因此也不调用析构函数,只在main函数结束或调用exit函数结束程序时,才调用static局部对象的析构函数。
-
如果定义了一个全局对象,则在程序的流程离开其作用域时(如main函数结束或调用exit函数) 时,调用该全局对象的析构函数。
-
如果用new运算符动态地建立了一个对象,当用delete运算符释放该对象时,先调用该对象的析构函数。
-
析构函数不返回任何值,没有函数类型,也没有函数参数。因此它不能被重载。一个类可以有多个构造函数,但只能有一个析构函数。
-
析构函数的作用并不是删除对象,而是在对象被册除(释放)时被调用的一个过程,以便在撤销对象占用的内存之前完成一些清理工作,使这部分内存可以被程序分配给新对象使用。
-
派生类是不能继承基类的析构函数的,也需要通过派生类的析构函数去调用基类的析构函数。
构造与释构函数执行顺序:
类在创建时:先依次执行上一父类的构造函数,再执行当前类构造函数。即类本身的构造函数是最后执行的。
基类构造-->父1构造-->…….-->最近的父类构造-->本类(当前类)构造
类在释放时:先执行本类释构函数。再依次执行父类的释构函数。如果有多层继承的,最后执行是最基类的释构。
本类(当前类)释构-->最近的父类释构-->…….--> 父1释构-->基类构造
类的函数成员的一些说明:
static静态函数:
在类的函数声明前加上 static关键字。即该函数就成了类的静态函数。他会在程序运行时,就为该函数声明内存。
静态函数,是类本身的函数。直接用类名加作用域符::就可以调用,如: CTest::myFunc();
不需在类的实创建后才能调用。这跟类的一般函数不同。他也可以在类的实体创建后,像使用一般函数一样使用,如 A.myFunc();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
class CTest { public : static void fn() { puts ( "TEST" ); } static void fn( int x) // 像普通函数一样, 虚函数可重载。 { cout << "TEST:" << x << endl; } }; void main() { CTest::fn(); // 直接引用静态函数 CTest::fn(100); CTest A; // 类的实体 A.fn(); // 实体引用静态函数 A.fn(200); } |
调用父类虚函数
调用父类的虚函数,在派生类作用域中可以用:this-><类名>::<函数名>();或 :: <类名>::<函数名>();在实体中可以用实例名加成员符 (.)或(->)<类名>::<函数名>
如示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
class CFather { public : virtual void Say() // 父类的虚拟函数 { puts ( "father say" ); } }; class CChild : public CFather { public : void Say() // 函数被重载 { ::CFather::Say(); //也可写成 this->CFather::Say(); puts ( "Child say" ); } }; int _tmain( int argc, _TCHAR* argv[]) { CChild C; C.Say(); C.CFather::Say(); //实体引用父类虚函数 system ( "pause" ); } |
纯虚函数
纯虚函数是在声明虚函数时被“初始化”为0的函数。声明纯虚函数的一般形式是
virtual 函数类型 函数名 (参数表列) = 0;
如:
1
2
3
4
5
6
7
|
class CTest { public : CTest(); // 构造函数 protected : virtual float myFunc( ) const =0; //纯虚函数 } |
纯虚函数只有函数的名字而不具备函数的功能,不能被调用。它只是通知编译系统:“在这里声明一个虚函数,留待派生类中定义”。在派生类中对此函数提供定义后,它才能具备函数的功能,可被调用。纯虚函数只是了实现多态性。
关于纯虚函数需要注意的几点:
-
纯虚函数没有函数体;
-
最后面的“=0”并不表示函数返回值为0,它只起形式上的作用,告诉编译系统“这是纯虚函数”;
-
这是一个声明语句,最后应有分号。