一、由编译器生成的成员函数
1)默认的构造函数
默认构造函数定义为没有参数,或者有默认的参数值。当用户自己未定义时,系统可以提供。
自动生成的默认构造函数,会调用继承的基类的默认构造函数来构造派生类的基类部分。
若Star是一个类,则
Star orig;
Star array[6];都将需要默认构造函数。
如果自己定义了构造函数,则系统不会再生成默认构造函数,这个时候最好自己要定义一个默认构造函数。
构造函数 --
【确保对象的生成】 -- 最好提供显式的默认构造函数,以确保对象能够正确的初始化为合理值。
2)复制构造函数
Star类的复制构造函数圆形如下:
Star(const Star &)
通常,我们会在一下情况下用到复制构造函数:
* 将新对象初始化为一个同类对象
* 函数按值传递对象参数
* 函数返回对象(而不是对象的引用)
* 生成临时对象
如果自己没有显式的定义复制构造函数,编译器将会自动生成一个其原型(但不提供函数定义??),以使得新对象的的每个成员初始化为原始对象相应的成员。
但是,当出现需要深度复制的情况时(使用new构造成员),或者需要修改的静态变量,这个时候一定要自己定义复制构造函数!!
例如:
string(const string & s)
{
// new char []
}
3)赋值运算符
在同类对象之间进行赋值操作,但不要把赋值和复制初始化混淆!!
【如果语句创建新对象,一般是复制初始化构造;语句修改已有对象,则是赋值】
Star orig;
Star pre = orig;
// copy ctor
Star post;
post = pre;
// copy assignment
赋值运算符返回对象的引用(使得可以连续赋值)。
orig = pre = post;
这是string类的赋值运算符重载:(见《C++
string类字符串的常用操作及实现》,同时实现“自我赋值”和"异常安全处理")
string & string::operator=(const string &) const ;
二、类设计的其它注意事项
1)构造函数不能够被继承(使用),只有自己需要生成对象时被使用。继承的派生类构造函数,会调用基类的构造函数。
2)析构函数,在使用new动态生成成员变量是一定要显式定义析构函数;当基类中存在virtual或者pure virtual时,将析构函数也定义为virtual。
3)转换构造函数,如果你不希望出现隐式的类型转换,使用【explicit】声明函数,但仍然可以使用显式的强制转换。
例如:
explict string(const char *);
4)按值传递和按引用传递
我们知道,一般情况下在类设计中都是用按引用传递,按值传递会隐式的调用复制构造函数和析构函数,在返回大型类时极其影响效率。可以节省时间和内存!!
按引用传递的另外原因:在继承使用虚函数时,被定义接受基类引用的参数可以接受派生类的引用。
5)const的使用
尽可能的使用const,但要注意场合。确保const参数在传递过程中不会修改其属性(常量),【可以将非const传给const参数,但禁止将const传给非const】。
三、共有继承的注意事项
1)IS-A关系式典型的基类 - 派生(is a king of),HAS-A有种接口的赶脚,接口的实现(is implemented as a),用友元函数实现(uses a)。纯自己的感觉,有误请指正。
2)基类的构造函数、析构函数和(一般情况下)赋值函数都不能继承。可以将派生类赋值给基类,反过来则maybe。
3)对于公共用户,使用保护成员和私有成员一样;对于派生类而言,使用保护乘员和公有成员一样。
派生类可以直接访问保护乘员,但只能通过基类的共有方法访问私有成员,相对来讲私有成员具有更好的安全性。
保护成员则可以简化编码,提高访问速度,但是这会使得派生类直接访问和修改基类的保护成员。
【使用私有成员比保护成员更好(尽可能的使用),但是保护方法很有用】
4)虚方法
设计基类时将方法定义为虚,使得【多态】派生类能够重新定义方法,这样可以使用【动态联编】。
纯虚函数使得类ABC【只定义接口,不涉及实现】,不能生成真正的对象。
5)友元函数
友元函数不是类的方法,因此不能够继承,在派生类中需要重新定义。
如果要使用基类的friend 函数,一般我们会使用【强制转换】,将派生类的指针或者引用转换成基类的指针或引用。
-- 【C++ Primer Plus】温习小结。
C++类设计过程中的原则(总结)