1. 派生类中的默认成员函数
-
6个默认成员函数:构造函数、析构函数、拷贝构造函数、赋值重载、取地址重载(普通对象和const对象取地址)
-
派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员,如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显式调用;
-
派生类拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化;
-
派生类的operator=必须要调用基类的operator=完成基类的复制;
-
派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员;
-
派生类对象初始化先调用基类构造再调用派生类构造;
-
派生类对象析构清理先调用派生类析构,再调用基类的析构。
//成员初始化顺序,一定先初始化父类成员,后初始化子类成员
class Person
{
public:
//构造函数
Person(int id=1, int age=16)
:_id(id)
, _age(age)
{
cout << "Person(int, int)" << endl;
}
//无缺省值构造函数
/*Person(int id, int age)
:_id(id)
, _age(age)
{
cout << "Person(int, int)" << endl;
}*/
//拷贝构造
Person(const Person& p)
:_id(p._id)
,_age(p._age)
{
cout << "Person(const Person&)" << endl;
}
//赋值运算符重载
Person& operator=(const Person& p)
{
if (this != &p)
{
_id = p._id;
_age = p._age;
}
cout << "Person& operator=(const Person& p)" << endl;
return *this;
}
//析构函数
~Person() {
cout << "~Person()" << endl;
}
protected:
int _id;
int _age;
};
class Student :public Person
{
public:
//不能在子类的初始化列表中直接初始化父类的成员
//如果父类有默认构造,初始化列表可以不用显式调用父类的构造函数,编译器会自动调用父类构造
//如果父类没有默认构造,必须显式调用父类的一个构造函数
Student(int id,int age,int stuId)
:_stuId(stuId)
{
//Person(id, age);
cout << "Student(int, int, int)" << endl;
}
//父类无缺省值时
/*Student(int id, int age, int stuId)
:Person(id, age)
, _stuId(stuId)
{
cout << "Student(int, int, int)" << endl;
}*/
//拷贝构造的初始化列表中可以调用普通的父类构造函数
//也可以调用拷贝构造
Student(const Student& stu)
:Person(stu)
,_stuId(stu._stuId)
{
cout << "Student(const Student&)" << endl;
}
Student& operator=(const Student& stu)
{
if (this != &stu)
{
//父类的赋值和子类的赋值运算符构成同名隐藏,需要加上父类作用域
Person::operator=(stu);
//operator=(stu);递归调用自己,同名隐藏,直到栈溢出才停止
_id = stu._id;
_age = stu._age;
_stuId = stu._stuId;
}
cout << "Student& operator=(const Student& stu)" << endl;
return *this;
}
//析构
//编译器在任何情况下,都会自动调用父类的析构,所以不需要显式调用父类析构
~Student()
{
//~Person();子类析构和父类析构构成同名隐藏,析构函数底层的函数名:destruct
//Person::~Person();
cout << "~Student()" << endl;
}
private:
int _stuId;
};
void test()
{
//子类的默认构造会自动调用父类的构造函数
//先父类后子类
Student stu(1, 2, 3);
//子类默认的拷贝构造会自动调用父类的拷贝构造
//如果子类显式定义了拷贝构造,子类的拷贝构造会自动调用父类的默认构造
Student copy(stu);
//赋值:子类的默认赋值运算符会自动调用父类的赋值运算符
//子类定义的赋值运算符不会自动调用父类的赋值运算符,除非显式调用
stu = copy;
//析构:子类默认的析构/显式调用都会自动调用父类的析构
//先子类后父类
Student stu1(1, 2, 3);
}
2. 继承与友元、静态成员
- 友元关系不能继承,基类友元不能访问子类私有的和保护的成员。
- 基类定义了static静态成员,则整个继承体系中只有一个这样的成员,无论派生多少个子类,都只有一个static成员实例。
//基类的static成员在整个继承体中,被所有对象共享
class A
{
public:
A()
{
++_a;
}
public:
static int _a;
};
int A::_a = 0;
class B :public A
{};
class C :public B
{};
void test()
{
A a;
B b;
C c;
cout << a._a << endl;
cout << b._a << endl;
cout << c._a << endl;
cout << &a._a << endl;
cout << &b._a << endl;
cout << &c._a << endl;
}