1 派生类的构造函数、析构函数的调用
1.1 派生类构造函数各部分的执行次序为:
1.调用基类构造函数,按它们在派生类定义的先后顺序,顺序调用。
2.调用成员对象的构造函数,按它们在类定义中声明的先后顺序,顺序调用。
3.派生类的构造函数体中的操作。
4.析构函数的调用顺序与构造函数相反,与构造函数的调用对应的是一个出栈的过程
1.2 若派生类中有虚继承,则先调用虚基类的构造函数
在派生类对象的创建中,首先是虚基类的构造函数并按它们声明的顺序构造。第二批是非虚基类的构造函数按它们声明的顺序调用。第三批是成员对象的构造函数。最后是派生类自己的构造函数被调用
2 构造子类对象时基类构造函数的注意点
子类在实例化对象时,需要先调用父类的构造函数,默认的是调用父类的无参构造函数,若父类没有无参构造函数,则需要在子类的构造函数处以初始化列表的形式调用父类的有参构造函数
1 父类有默认的构造函数
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
class Base
{
public:
Base(int data = 0) :x(data)
{
}
public:
int x;
};
class D:public Base
{
public:
D(int data = 0)
{
y = data;
}
public:
int y;
};
int main()
{
// 父类提供了默认的构造函数,可以直接实例化出子类
D d;
cout << d.x << endl;
cout << d.y << endl;
system("pause");
return 0;
}
2 父类没有默认的构造函数
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
class Base
{
public:
Base(int data) :x(0)
{
x = data;
}
public:
int x;
};
class D:public Base
{
public:
// 父类没有默认的构造函数,需要在子类的构造函数处列表初始化调用父类的有参构造
// 若子类中有父类对象作为成员,也需要在此处一并初始化
D(int data = 0) :Base(data),b(data)
{
y = data;
}
public:
Base b;
int y;
};
int main()
{
// 父类没有默认的构造函数,需要在子类的构造函数处列表初始化调用父类的有参构造
D d(5);
cout << d.x << endl;
cout << d.y << endl;
system("pause");
return 0;
}
3 多重继承(菱形继承)中子类对象调用父类成员的注意点
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
// 初始基类,菱形继承的顶点
class Base
{
public:
void fun()
{
cout << "Base" << endl;
}
int n;
};
// 虚拟继承Base 写成虚拟继承的作用是若还有其他的类继承自Base,则Base1与同虚拟继承自
// Base的类共享继承而来的成员空间,不会单独各自拷贝一份,作用类似类中的静态成员
class Base1:virtual public Base
{
public:
void fun()
{
cout << "Base1" << endl;
}
int x;
};
// 与Base1 同虚拟继承自Base,共享继承自Base成员的空间
class Base2 :virtual public Base
{
public:
void fun()
{
cout << "Base2" << endl;
}
int y;
};
// 多继承Base1 Base2 ,最终所有的类构成菱形继承
class D :public Base1, public Base2
{
public:
void fun()
{
cout << "D" << endl;
}
int z;
};
int main()
{
D d;
// 若没有虚拟继承,此处会有二义性报错,不知道成员n是来自Base1还是Base2
d.n = 0;
// 限定类的作用域,指明是继承自Base1或Base2的成员n,
// 若是这样指定,即使没有虚继承,也不会有二义性错误,但是成员n有不同的地址空间
d.Base1::n = 10;
d.Base2::n = 20;
cout << d.n << endl;
cout << d.Base1::n << endl;
cout << d.Base2::n << endl;
system("pause");
return 0;
}
4 派生类与基类的赋值兼容规则
- 派生类的对象可以赋值给基类的对象,这时是把派生类对象中 从对应基类中继承来的隐藏对象赋值给基类对象。反过来不行,因为派生类的新成员无值可赋。
- 可以将一个派生类的对象的地址赋给其基类的指针变量,但只能通过这个指针访问派生类中由基类继承来的隐藏对象,不能访问派生类中的新成员。同样也不能反过来做。
- 派生类对象可以初始化基类的引用。引用是别名,但这个别名只能包含派生类对象中的由基类继承来的隐藏对象
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
class Base
{
public:
void fun()
{
cout << "Base fun()" << endl;
}
// 父类fun函数重载
void fun(int)
{
cout << "Base fun(int)" << endl;
}
};
class D :public Base
{
public:
// 子类同名函数fun
void fun()
{
cout << "D fun()" << endl;
}
};
int main()
{
D d;
// 子类对象调用同名成员函数时,会调用子类自身的成员函数fun,继承自父类的,
// 函数fun() fun(int)被覆盖掉,若通过子类对象调用继承自父类的成员函数fun(int)
// 程序会报错,因为父类的函数fun(int)被子类自己的fun()函数覆盖,不会重载
// 如果子类没有同名的fun函数,则可以重载调用
d.fun();
//d.fun(0); error
// 通过子类指针或引用赋值后再调用,则会调用父类的成员函数
// 当然,要是把父类的成员函数都写成虚函数,通过子类指针或引用赋值后再调用则
// 又会调用子类自身的成员函数,即多态
Base *b1 = &d;
b1->fun();
b1->fun(0);
Base &b2 = d;
b2.fun();
b2.fun(0);
system("pause");
return 0;
}