类的继承——继承与特殊成员函数
● 派生类合成的……
– 缺省构造函数会隐式调用基类的缺省构造函数
– 拷贝构造函数将隐式调用基类的拷贝构造函数
– 赋值函数将隐式调用基类的赋值函数
struct Base
{
Base()
{
std::cout << "Base()\n";
}
Base(const Base& val)
{
std::cout << "Base(const Base& val)\n";
}
Base& operator= (const Base&)
{
std::cout << "Base& operator= (const Base&)\n";
return *this;
}
};
struct Derive : Base
{
//没有定义派生类的缺省构造函数,编译器自动合成了一个缺省构造函数
//没有定义派生类的拷贝构造函数,编译器自动合成了一个拷贝构造函数
//没有定义派生类的重载赋值运算符函数,编译器自动合成了一个重载赋值运算符函数
};
int main()
{
Derive d;
Derive x(d);
x = d;
return 0;
}
● 派生类的析构函数会调用基类的析构函数
struct Base
{
Base()
{
std::cout << "Base()\n";
}
Base(const Base& val)
{
std::cout << "Base(const Base& val)\n";
}
Base& operator= (const Base&)
{
std::cout << "Base& operator= (const Base&)\n";
return *this;
}
~Base()
{
std::cout << "~Base()\n";
}
};
struct Derive : Base
{
// Derive() = default;
// Derive(const Derive& val) = default;
// Derive& operator= (const Derive&) = default;
};
int main()
{
Derive d;
return 0;
}
struct Base
{
Base()
{
std::cout << "Base()\n";
}
Base(const Base& val)
{
std::cout << "Base(const Base& val)\n";
}
Base& operator= (const Base&)
{
std::cout << "Base& operator= (const Base&)\n";
return *this;
}
~Base()
{
std::cout << "~Base()\n";
}
};
struct Derive : Base
{
~Derive()
{
std::cout << "~Derive()\n";
}
};
int main()
{
Derive d;
return 0;
}
● 派生类的其它构造函数将隐式调用基类的缺省构造函数
struct Base
{
Base()
{
std::cout << "Base()\n";
}
Base(int) //参数有明确的意义
{
std::cout << "Base(int)\n";
}
Base(const Base& val)
{
std::cout << "Base(const Base& val)\n";
}
Base& operator= (const Base&)
{
std::cout << "Base& operator= (const Base&)\n";
return *this;
}
~Base()
{
std::cout << "~Base()\n";
}
};
struct Derive : Base
{
Derive(int)
{
std::cout << "Derive(int)\n";
}
//Derive(const Derive&) = default; //隐式调用基类的缺省构造函数
Derive(const Derive&) //自定义派生类拷贝构造函数,编译器最佳的选择是隐式调用基类的缺省构造函数,一些情况下可能不合理
{
std::cout << "Derive(const Derive&)\n";
}
};
int main()
{
Derive d(0);
Derive x(d);
return 0;
}
● 所有的特殊成员函数在显式定义时都可能需要显式调用基类相关成员
struct Base
{
Base()
{
std::cout << "Base()\n";
}
Base(int input) //参数有明确的意义
{
std::cout << "Base(int)\n";
}
Base(const Base& val)
{
std::cout << "Base(const Base& val)\n";
}
Base& operator= (const Base&)
{
std::cout << "Base& operator= (const Base&)\n";
return *this;
}
~Base()
{
std::cout << "~Base()\n";
}
};
struct Derive : Base
{
Derive() //派生类的缺省构造函数包括三部分,第一部分: 声明
: Base(0) //第三部分: 处理隐式的或者是显式的初始化列表
{
//第二部分: 不是初始化,是对已初始化好的内存进行赋值
}
//Derive(const Derive&) = default; //隐式调用基类的缺省构造函数
Derive(const Derive& input)
: Base(input)
{
}
Derive& operator= (const Derive& val)
{
Base::operator=(val);
return *this;
}
};
int main()
{
Derive d;
Derive x(d);
x = d;
return 0;
}
● 构造与销毁顺序
– 基类的构造函数会先调用,之后才涉及到派生类中数据成员的构造
构造基类的时候不会依赖派生类的成员,而构造派生类的时候可能会依赖基类的成员
– 派生类中的数据成员会被先销毁,之后才涉及到基类的析构函数调用
#include<iostream>
using namespace std;
class Date
{
int Year, Month, Day;
public:
Date(int y = 2002, int m = 1, int d = 1)
{
Year = y; Month = m; Day = d;
cout << "Constructor: ";
ShowDate();
}
void ShowDate()
{
cout << Year << '.' << Month << '.' << Day << endl;
}
~Date()
{
cout << "Destructor: ";
ShowDate();
}
};
void main()
{
Date d1(2008, 1, 1);
Date d2(2008, 2, 2);
Date d3(2008, 3, 3);
}
Date d4(2008, 4, 4);
Date d5(2008, 5, 5);
#include<iostream>
using namespace std;
class Date
{
int Year, Month, Day;
public:
Date(int y = 2002, int m = 1, int d = 1)
{
Year = y; Month = m; Day = d;
cout << "Constructor: ";
ShowDate();
}
void ShowDate()
{
cout << Year << '.' << Month << '.' << Day << endl;
}
~Date()
{
cout << "Destructor: ";
ShowDate();
}
};
Date d5(2008, 5, 5);
Date d6(2008, 6, 6);
static Date d7(2008, 7, 7);
static Date d8(2008, 8, 8);
void fun()
{
cout << "进入fun()函数!\n";
Date d9(2008, 9, 9);
Date d10(2008, 10, 10);
static Date d11(2008, 11, 11);
static Date d12(2008, 12, 12);
cout << "退出fun()函数!\n";
}
void function()
{
cout << "进入function()函数!\n";
Date d13(2008, 13, 13);
Date d14(2008, 14, 14);
static Date d15(2008, 15, 15);
static Date d16(2008, 16, 16);
cout << "退出function()函数!\n";
}
void main()
{
cout << "进入main()函数!" << endl;
Date d1(2008, 1, 1);
Date d2(2008, 2, 2);
static Date d3(2008, 3, 3);
static Date d4(2008, 4, 4);
fun();
fun();
function();
function();
cout << "退出main()函数!" << endl;
}
构造函数和析构函数的调用顺序与撤销顺序:
1、如果创建了全局对象,那么全局对象最先(进入main函数之前)调用构造函数,按全局对象的声明顺序依次调用构造函数,与全局对象的位置无关:可以在所有的函数之前,也可以在主函数后。
2、局部对象按创建的先后顺序依次调用构造函数。
3、在主函数外的函数中,如果创建了局部动态对象,函数结束,立刻撤销局部动态对象。如果创建了局部静态对象,函数结束不会立刻撤销。
4、主函数结束后,先撤销主函数中的动态对象。
5、最后撤销所有的静态对象(全局和局部),按静态对象创建顺序的相反顺序撤销(栈内存)。
参考
深蓝学院:C++基础与深度解析
C++程序设计教程(第3版) 王珊珊 臧洌 张志航 编著
C++ Primer Plus(第6版)中文版