1.默认构造函数
默认构造函数要么没有参数,要么所有的参数都是默认值。
如果没有定义任何构造函数,编译器将定义默认构造函数,使我们能够创建对象。
例:假设Star是一个类,则下述代码需要使用默认构造函数:
Star rigel;
Star pleiades[10];
自动生成的默认构造函数的另一项功能是,调用基类的默认构造函数以及调用本身是对象的成员所属类的默认构造函数。
另外,如果派生类构造函数的成员初始化列表中没有显示调用基类构造函数,则编译器将使用基类的默认构造函数来构造派生类对象的基类部分。在这种情况下,如果基类没有构造函数,将导致编译阶段错误。
如果定义了某种构造函数,编译器将不会定义默认构造函数。
提供构造函数的目的是确保对象总能够被正确的初始化。如果类包含指针成员,则必须初始化这些成员。因此,最好提供一个显示默认构造函数,将所有的类数据成员都初始化为合理的值。
2.拷贝构造函数
拷贝构造函数接受其所属类的对象作为参数。
例:Star类的复制构造函数原型如下:
Star(cosnt Star &);
在下述情况下,将使用拷贝构造函数:
1.将新对象初始化为一个同类对象。
2.按值将对象传递给函数。
3.函数按值返回对象。
4.编译器生成临时对象。
如果程序没有使用(显式或隐式)拷贝构造函数,编译器将提供原型,但不提供函数定义;否则,程序将定义一个执行成员初始化的拷贝构造函数(新对象的每个成员都被初始化为原始对象相应成员的值)。如果成员为类对象,则初始化改成员时,将使用相应类的拷贝构造函数。
3.赋值运算符
默认的赋值运算符用于处理同类对象之间的赋值。语句创建新的对象,则使用初始化;语句修改已有的对象,则是赋值。
Star sirius;
Star alpha = sirius;//初始化
Star dogstar;
dogstar = sirius;//赋值
Star类的赋值运算符原型如下:
Star &Star::operator=(const Star &);
4.构造函数
构造函数不同于其他类方法,因为它是创建新对象,而其他类方法是被现有的对象调用。
这也是为什么构造函数不能被继承的原因之一。
继承意味着派生类的对象能够使用基类的方法。然而构造函数在完成其工作任务之前,对象不存在。
5.析构函数
一定要显示析构函数来释放类构造函数使用new分配的所有内存,并完成类对象所需的任何特殊的清理工作。
对于基类,即使不需要析构函数,也要提供一个虚析构函数。
6.按值传递对象和按引用传递对象
编写使用对象作为引用参数的时候,应按引用而不是按值来传递对象。这样做也是为了提高效率。
按值传递对象设计到生成临时拷贝,即调用拷贝构造函数,然后调用析构函数。调用这些函数需要时间,复制大型对象比传递引用花费的时间要多得多。如果函数不修改对象,应将参数声明为const引用。
按引用传递对象的另一个原因是,在继承使用虚函数时,被定义接受基类引用参数的函数可以接受派生类。
7.返回对象和返回引用
返回引用而不是返回对象的原因在于,返回对象涉及生成返回对象的临时副本,这是调用函数的程序可使用的副本。
返回对象的时间成本包括调用拷贝构造函数来生成副本所需要的时间和调用析构函数删除副本所需要的时间。
返回引用可以节省时间和内存。直接返回对象与按值传递对象相似:它们都生成临时副本。
规则:
如果函数返回在函数中创建的临时对象,则不要使用引用。
例:
Vector Vector::operator+(const Vector & b) const
{
return Vector(x+b.x,y+b.y);
}
如果函数返回的是通过引用或指针传递给它的对象,则应按引用返回对象。
const Stock&Stock::topval(const Stock& s)const
{
if(s.total_val > total_val)
return s;
else
return *this;
}
8.使用const
1.用它来确保方法不修改参数:
Star::Star(const char* s){.....}
2.使用const来确不修改调用它的对象:
void Star::show() const {....}
9.公有继承的考虑因素
9.1is-a关系
派生类对象也是一个基类对象。
例如:香蕉是一种水果。那么我们可以从Friut类中派生出banana类。
banana is a kind of friut。
基类的指针指向派生类的对象也就是继承。
菱形继承最好的办法是创建包含纯虚函数的抽象数据类,并从它派生出其他的。
9.2不能被继承
1.构造函数:
创建派生类对象时,必须调用派生类的构造函数,然而,派生类构造函数通常使用初始化成员列表来调用基类构造函数,以创建派生类对象的基类部分。
2.析构函数:
在释放对象时,程序将首先调用派生类的析构函数,然后调用基类的析构函数。
3.赋值运算符:
派生类继承的方法的特征标与基类完全相同,但赋值运算符的特征标碎类而异,这是因为她包含一个类型为其所属类的形参。
9.3私有成员与保护成员
对派生类而言:保护成员类似于私有成员;
对于外部而言:保护成员与私有成员类似。
派生类可以直接访问基类的保护成员,但只能通过基类的成员函数来访问私有成员。
9.4虚方法
设计基类时,必须确定是否将类方法声明为虚的。如果希望派生类能够重新定义方法,则硬在基类中将方法定义为虚的。
9.5析构函数
基类的析构函数应当是虚的。当通过指向对象的基类指针或引用来删除派生对象时,程序将首先调用派生了的析构函数,然后调用基类的析构函数,而不仅仅是调用基类的析构函数。