C++ note (10)

继承和派生 Ⅲ
类型兼容性
赋值运算的类型兼容性
- 可以将后代类的对象赋值给祖先类对象,反之不可。
- 每个派生类对象包含一个基类部分,这意味着可以将派生类对象当作基类对象使用。
1
2
3
4
5
6
7--------- -----------
| i | <- | i |
| j | <- | j |
| x_tmp | <- | x_tmp |
--------- | nmember |
obj1 -----------
obj21
2
3
4base obj1;
y1 obj2;
obj1 = obj2; // 把obj2中基类部分的内容赋给obj1
obj2 = obj1; // wrong赋值运算符必须是成员函数实现。因此,赋值运算总是使用左操作数类型的赋值运算。
因此,子类对象赋值给父类变量是正确的,但会放弃派生扩展的内容。反之,必须从语法层面禁止父类对象赋值子类。
如果需要完成 obj2 = obj1,必须显式给出
*(base *)(&obj2)= obj1
y1继承base,且
base obj1
y1 obj2
- 指向基类对象的指针也可指向公有派生类对象
1
2
3
4
5
6
7base *p;
y1 *p;
p = &obj1; // ok
p1= &obj1; // wrong
p = &obj2; // ok
p1= &obj2; // ok
p = p1; // ok - 只有公有派生类才能兼容基类类型(上述规则只适用于共有派生)
举例
1 | // b.h |
1 | // b.cpp |
1 | base::display() |
类的类型转换
Upcasting and Downcasting
Upcasting(向 上/基 类型转换)
- Assigning a pointer of a derived class type to a pointer of its base class type. Done implicitlyDowncasting(向 下/派生 类型转换)
1
2
3geometricObj *p = new circle(1);
circle *p1 = new circle(2);
p = p1; - Assigning a pointer of a base class type to a pointer of its derived class type
- Done explicitly using dynamic_cast
1
p1 = dynamic cast<circle *>(p);
Dynamic Casting
The display function :
1 | // A function for displaying a geometric object |
1 | // A function for displaying a geometric object |
Casts
static_cast
- Used to convert one data type to another and hands all reasonable castsconst_cast
1
2average = (float) hits / (float) at_bats;
average = static_cast<float>(hits) / static_cast<float>(at_bats); - Used to cast away constness.
1
2
3
4
5
6
7
8
9
10
11
12
using namespace std;
int main() {
const int i = 100;
const int *p = &i;
int *q = const_cast<int*>(p);
int j = i;
cout << i << endl << j<<endl
<< *p << endl << *q <<endl;
return 0;
}很坑,请按案例。
Const 转为 非 const,
(1)c语言中,const 默认解释为 常量或字面量,所以转后也无法修改;
(2)解释为,编译生成一个temp可变量,给你修改
1 |
|
reinterpret_cast
- converts between unrelated types such as an integer to a pointer or a pointer to an unrelated pointer type.
1
2
3
4
5
6
7
8int *ip;
char *cp;
void *gp;
cp=ip;
ip=cp;
cp=gp;
gp=cp;
cp=reinterpret_cast<char*>(gp);请打开代码中注释,编译去掉出错的语句,解释出错原因:
(1)有类型指针间,不能隐式转换,除非向上转换;
(2)有类型指针可隐式转为通用类型指针,反之不行;
(3)其他需要显式转换,c++建议用 reinterpret 转指针
dynamic_cast
- Used for casting across or within inheritance.
- This cast is used with classes having virtual functions.
多重继承
C++支持的多继承
多重继承:派生类继承多个基类
- 代表概念:C既 is a A 又 is a B重复继承:菱形继承
1
2
3class 派生类名 : 继承控制1, 基类名1, 继承控制2, 基类名2, ... {
成员声名;
} - 多重继承特例,base A被派生两次以上
举例——device
1 | class device1 { |
1 | int main() { |
1 | The weight of the device : 0.7 |
虚基类
继承基类时,在继承访问控制前添加保留字 “virtual”。 那么这个基类就是一个虚拟基类。
虚拟基类用于共享继承。
普通基类与虚基类之间的唯一区别只有在派生类重复继承了某一基类时才表现出来。
若派生类有一个虚基类作为祖先类,则在派生类构造函数中需要列出对虚基类构造函数的调用(否则,调用虚基类的默认构造函数),且对虚基类构造函数的调用总是先于普通基类的构造函数。
创建后代类对象时,当该后代类列出的虚基类构造函数被调用,Virtual关键字保证了虚基类的唯一副本只被初始化一次。
创建派生类对象时构造函数的调用次序:
- 最先调用虚基类的构造函数;
- 其次调用普通基类的构造函数,多个基类则按派生类声明时列出的次序、从左到右调用,而不是初始化列表中的次序;
- 再次调用对象成员的构造函数,按类声明中对象成员出现的次序调用,而不是初始化列表中的次序
- 最后执行派生类的构造函数。
举例:
1 | class base { |
举例:
1 | class baseA { |
1 | This is baseA class. |
构造顺序:
- 先基类后成员
- 先虚后实
- 先左后右
析构顺序:
- 与构造顺序相反