上篇博客 C++中的继承(上) 我们讲到了继承关系函数重载与函数重定义等概念,接下来我们继续介绍继承与派生:
继承与派生
派生类中的默认成员函数
- 派生类的构造函数必须调基类的构造函数初始化基类那一部分的成员。若基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
- 派生类的拷贝构造函数必须调基类的拷贝构造函数完成基类部分成员的拷贝构造。
- 派生类的operator=必须调用基类的operator=完成基类部分成员的赋值拷贝。
- 派生类的析构函数会在被调用完之后自动调用基类的析构函数清理基类成员,因为对象都是被创建在栈帧中的,栈中的对象时先创建的后销毁,这样调用才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
- 派生类对象先调用基类构造函数再调用派生类构造。
- 派生类对象先调用派生类析构函数再调用基类析构函数。
注:必须在初始化列表中初始化的几种情况
《C++ Primer》中提到在以下三种情况下需要使用初始化成员列表:
情况一、需要初始化的数据成员是对象的情况(这里包含了继承情况下,通过显示调用父类的构造函数对父类数据成员进行初始化);
情况二、需要初始化const修饰的类成员或初始化引用成员数据;
情况三、子类初始化父类的私有成员;
下面我简单的画一个图来解释一下派生类对象的各个行为
举个栗子
class Person
{
public:
Person(const char* name = "peter")
: _name(name)
{
cout << "Person()" << endl;
}
Person(const Person& p)
: _name(p._name)
{
cout << "Person(const Person& p)" << endl;
}
Person& operator=(const Person& p)
{
cout << "Person operator=(const Person& p)" << endl;
if (this != &p)
_name = p._name;
return *this;
}
~Person()
{
cout << "~Person()" << endl;
}
protected:
string _name; // 姓名
};
class Student : public Person
{
public:
Student(const char* name, int num)
: Person(name)
, _num(num)
{
cout << "Student()" << endl;
}
Student(const Student& s)
: Person(s)
, _num(s._num)
{
cout << "Student(const Student& s)" << endl;
}
Student& operator = (const Student& s)
{
cout << "Student& operator= (const Student& s)" << endl;
if (this != &s)
{
Person::operator =(s);
_num = s._num;
}
return *this;
}
~Student()
{
cout << "~Student()" << endl;
}
protected:
int _num; //学号
};
void Test()
{
Student s1("jack", 18);//构造函数
Student s2(s1);
Student s3("rose", 17);
s1 = s3;
}
int main()
{
Test();
return 0;
}
这里我们可以清楚的看到一个派生类对象在创建和销毁的时候是如何调用对应构造函数和析构函数的。
继承和友元
注:友元关系不能被继承,因为友元函数能访问类的私有成员,若是友元关系可以被继承的话,那么基类就能直接访问派生类的私有和保护成员了,这在C++中是不被允许的。
继承和静态成员
注:基类定义了static静态成员,则整个继承体系里面只有一个这样的成员,不论基类被继承多少次,都只有一个static的成员实例,所有派生类访问的static静态成员均是同一个。