派生类不能直接访问基类的私有成员,必须通过基类方法进行访问。创建派生类对象时,首先创建基类对象。也就是说基类对象在程序进入派生类构造函数之前被创建。C++使用成员初始化列表来完成。派生类构造函数初始化基类私有成员,采用成员初始化列表。
总结:
首先,若基类函数需要被派生类重定义,则需要将其在基类声明为虚函数。
虚函数必须完全一致,否则派生类将重定义函数,隐藏基类虚函数。返回值除外
析构函数须使用虚析构函数,防止因指针或引用类型导致析构错误对象。
虚函数采用动态联编,不同位置虚函数存在不同地址,每个对象包含不同函数地址。
Class_name::Class_name(int a,int b, int c):Base_class(a,b) { }
派生类构造函数要点:
- 首先创建基类对象
- 派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数
- 派生类构造函数应初始化派生类新增的成员变量
注意: 成员初始化列表只能用于构造函数
派生类和基类的关系
1.1 派生类可使用基类的方法(非私有)
基类指针可以在不进行显示类型转换情况下指向派生类对象;基类引用可在不进行显示类型转换的情况下引用派生类对象。同样也可以将派生类对象赋给基类对象。
基类指针或引用只能用于调用基类方法,因此不能使用其调用派生类方法。 通常要求引用和指针类型与赋给的类型匹配,但这一规则对继承来说是例外。不可以将基类对象和地址赋给派生类引用和指针。
1.2 多态公有继承
概念: 同一个方法在派生类和基类中的行为是不同的,那么方法的行为应取决于调用该方法的对象,称为多态。
方法:
- 派生类中重新定义基类的方法
- 使用虚函数
要点: 若要在派生类重新定义基类方法,通常将基类方法声明 (定义不需要)为虚函数。为基类声明一个虚析构函数也是一种惯例。
重定义基类方法
- 在基类和派生类中分别定义一个同名函数,则根据对象类型来分别调用相应的函数。
class Student {
string name;
int age;
public:
Student(string a,int b):name(a),age(b){}
void Print() {
cout << "调用Student: "<<name << endl;
}
};
class Grade :public Student{
int grade;
public:
Grade(int a, string name, int b) :Student(name, b) {
grade = a;
}
void Print() {
cout << "调用grade: " << grade << endl;
}
};
可见函数Print()
在基类和派生类分别定义了,若对象调用 如下:
Student S("liming", 13);
S.Print();
Grade G1(10, "lama", 20);
G1.Print();
结果为:
调用Student: liming
调用grade: 10
- 方法通过引用或者指针而不是对象调用的,没有使用
virtual
定义,则根据引用类型或指针类型选择方法。
调用如下:
Student S("liming", 13);
S.Print();
Student *S1;
Grade G1(10, "lama", 20);
Student &S2 = G1;
S2.Print();
S1 = &G1;
S1->Print();
G1.Print();
结果为:
调用Student: liming
调用Student: lama
调用Student: lama
调用grade: 10
- 如果使用
virtual
程序将根据引用或指针指向的对象类型来选择方法。
将方法定义设置为:
virtual void Print() {
cout << "调用Student: "<<name << endl;
}
则
Student S("liming", 13);
S.Print();
Student *S1;
Grade G1(10, "lama", 20);
Student &S2 = G1;
S2.Print();
S1 = &G1;
S1->Print();
G1.Print();
结果为:
调用Student: liming
调用grade: 10
调用grade: 10
调用grade: 10
虚析构函数的作用
在使用new
和delete
的对象中,如果析构函数不是虚的,则只调用对应于指针类型的析构函数。
Student *S1 = new Grade(12, "ll", 17);
delete S1;
结果为:
调用Student构造函数:
调用grade构造函数:
调用student析构函数
由此可见,并没有调用派生类的析构函数。
若将基类析构函数设为vritual
,则结果如下:
调用Student构造函数:
调用grade构造函数:
调用Grade析构函数
调用student析构函数
所以应将析构函数设为虚析构函数。