继承与派生
1 继承
定义基类
class Quote{
public:
Quote() = default;
Quote(string book,double sales_price):book_no(book),price(sales_price){};
string isbn()const{
return this.book_no;
}
virtual double net_price(int n)const{
return n*price;
}
virtual ~Quote()=default;
private:
string book_no;
protected:
double price;
};
定义派生类
class Bulk_quote:public Quote{
public:
Bulk_quote()=default;
Bulk_quote(string,double,int,double);
double net_price(int n)const override;
private:
int mn_qty = 0;
double discount=0.0;
};
Bulk_quote::Bulk_quote(string book,double p,int qty,double disc):Quote(book,p),min_qty(qty),discount(disc){}//委托基类构造函数
继承方式
- 单一继承:继承一个父类,这种继承称为单一继承,一般情况尽量使用单一继承,使用多重继承容易造成混乱易出问题
- 多重继承:继承多个父类,类与类之间要用逗号隔开,类名之前要有继承权限,假使两个或两个基类都有某变量或函数,在子类中调用时需要加类名限定符如c.a::i = 1;
- 菱形继承:多重继承掺杂隔代继承1-n-1模式,此时需要用到虚继承,例如 B,C虚拟继承于A,D再多重继承B,C,否则会出错
继承权限
- 继承权限:继承方式规定了如何访问继承的基类的成员。继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sWQQdIlA-1693187663763)(image/2021-03-07-14-26-29.png)]
注意事项
- 基类的静态成员变量,在整个继承体系中只存在该成员的唯一定义。
- 派生类的声明中包含类名,但不能包含派生列表。
final关键字
-
使用关键字final说明符防止类被继承
-
使用关键字final说明符防止函数被重写
class A final{
void f1(int) const;
};
2 访问控制与继承
访问控制
- 派生类能够访问公有成员和受保护的成员。
- 派生类不能访问私有成员。
public:
private:
protected:
继承类型
- public 公有继承
- private私有继承
默认的集成保护级别
- class关键字定义的派生类,默认是私有继承
- struct关键字定义的派生类,默认是公有继承
3 类作用域与继承
作用域
- 当存在继承关系是,派生类的作用域嵌套在基类的作用域内。如果一个名字在派生类的作用域内无法解析,编译器在外层的基类作用域中寻找改名字的定义。
编译时名字查找
- 引用或指针的静态类型决定了该对象有哪些成员是可见的。一个基类的引用和指针只能访问基类的成员。即使动态对象是其派生类。
名字冲突与继承
- 派生了重用定义在直接基类或间接基类中的名字,会屏蔽定义在外层作用域基类中的名字
访问隐藏的成员
- 通过作用域运算符来使用一个被隐藏的基类成员。
4 类型转换与继承
静态类型和动态类型
- 静态类型在编译时已知。是指针或者引用的类型。
- 动态类型表示内存中的对象类型。动态类型直到运行时才可知。
派生类到基类的类型转换。
- 把派生类对象当成基类对象来使用。将基类的指针或引用绑定到派生类对象。
基类到派生类的类型转换。
- 不存在从基类向派生类的隐式转换。
对象之间不存在类型转换
- 所谓的类型转换只是指针或者引用的类型转换,对象本身的类型,没有发生改变。
- 但是派生类可以赋值给基类的对象。基类的拷贝构造函数和移动构造函数,接受一个引用对象。将派生类对象赋值给引用对象,实现基类的初始化。实际生成的是一个基类对象。
- 这里并非多态,而是执行拷贝构造函数。创建了一个基类对象
class A{
}
class B:public A{
}
B b();
A a = b;//可以赋值给基类对象。执行拷贝构造函数。并非多态。
A aa(b);//直接初始化,执行基类的拷贝构造函数。并非多态。
转换规则总结
- 从派生类向基类的类型转换只对指针或引用有效。是指针或引用的类型转换,而不是其指向的对象的类型发生改变。
- 基类向派生类不存在隐式类型转换
- 派生类向基类的类型转换也可能会由于访问受限而变得不可行。