存在继承关系的类型之间的转换规则
- 从派生类向基类的类型转换只对指针或引用类型有效。
- 基类向派生类不存在隐式转换。
- 和任何其他成员一样,派生类向基类的类型转换也可能会由于访问受限而变得不可行。
访问控制与继承
派生访问说明符对于派生类的成员(及友元)能否访问其直接基类的成员没有什么影响。对基类成员的访问权限只与基类中的访问说明符有关。 派生访问说明符的目的是控制派生类用户(包括派生类和派生类在内)对于基类成员的访问权限。
Pub_Derv 和 Priv_Derv 都可以访问受保护的成员prot_mem,同时它们都不能访问私有成员priv_mem。
class Base
{
public:
void pub_mem();
protected:
int prot_mem;
private:
char priv_mem;
};
class Pub_Derv :public Base
{
public:
int f()
{
return prot_mem;
}
};
class Priv_Derv :private Base
{
public:
int f1()const
{
return prot_mem;
}
};
- protected
1.和私有成员类似,受保护的成员对于类的用户来是说不可访问的。
2.和公有成员类似,受保护的成员对于派生类的成员和友元来说是可访问的。
3.派生类的成员或友元只能通过派生类对象来访问基类的受保护成员。
派生类向基类转换的可访问性
假定D继承自B:
- 只有当D公有(public)继承B时,用户代码才能使用派生类向基类的转换;如果D继承B的方式是受保护或者私有的,则用户代码不能使用该转换。
- 不论D以什么方式继承B,D的成员函数和友元都能使用派生类向基类的转化;派生类向其直接基类的转换类型对于派生类的成员和友元来说永远是可访问的。
- 如果D继承B的方式是公有的或者受保护的,则D的派生类的成员和友元可以使用D向B的转换;如果D继承B的方式是私有的,则不能使用。
继承中的类作用域
派生类的成员会隐藏同名的基类成员,即使形参列表不同,基类成员也会被隐藏。(除了覆盖继承而来的虚函数外,派生类最好不要重用其他定义在基类中的名字)
名字查找与继承
假定我们调用p->mem()(或者obj.mem() ),则依次执行以下几个步骤
- 首先确定p(或obj)的静态类型
- 在p(或obj)的静态类型对应的类中查找mem。如果找不到,则依次在直接基类中不断查找直到到达继承链顶端。
- 一旦找到了mem,就进行常规的类型检查以确认这次调用是否合法。
- 加入调用合法,则根据调用是否是虚函数而产生不同代码
—如果mem是虚函数且我们是通过引用或指针进行的调用,则编译器产生的代码将在运行时确定到底该运行虚函数的哪个版本,依据是对象的动态类型。
—反之,如果mem不是虚函数或者我们是通过对象(而非引用或指针)进行调用,编译器将产生一个常规的函数调用。[不会发生动态绑定,实际调用的函数版本由指针的静态类型决定]
Disc_quote
如果基类的析构函数不是虚函数,则delete一个指向派生类对象的基类指针将会产生未定义的行为。
一个基类总是需要析构函数,而且能将析构函数设为虚函数。
定义派生类的拷贝或移动构造函数
在默认情况下,基类默认构造函数初始化派生类对象的基类部分。如果我们想拷贝(或移动)基类部分,则必须在派生类的构造函数初始值列表中显式地使用基类的拷贝(或移动)构造函数。
派生类赋值运算符
D& D::operator=(const D& rhs)
{
Base::operator=(rhs); //为基类部分赋值
//按照往常的方式为派生类的成员赋值
//酌情处理自赋值及实方已有资源等情况
return *this;
}
移动操作与继承
大多数基类都会定义一个虚析构函数。默认情况下,基类通常不含有合成的移动操作,而且在派生类中也没有合成的移动操作。
基类缺少移动操作会阻止派生类拥有自己的合成移动操作。【如果定义了移动操作,必须同时定义拷贝操作】
class Quote
{
public:
Quote()=default; //对成员依次进行默认初始化
Quote(const Quote&)=default; //拷贝构造
Quote(Quote&&)=default; //移动构造
Quote& operator=(const Quote&)=default; //拷贝赋值运算符
Quote& operator=(Quote&&)=default; //移动赋值运算符
virtual ~Quote()=default;
};
继承的构造函数
一个类只初始化它的直接基类,出于同样原因,一个类也只继承其直接基类的构造函数。
类不能继承默认,拷贝和移动构造函数。
派生类继承基类构造函数的方式是提供一条注明了(直接)基类的using声名语句。
class Bulk_quote : public Disc_quote
{
public:
using Disc_quote::Disc_quote; //继承Disc_quote的构造函数
double net_price(size_t) const;
};
容器与继承
当派生类对象被赋值给基类对象时,其中的派生类部分将被“切掉”,因此容器和存在继承关系的类型无法兼容。
如果要在容器中存放具有继承关系的对象时,实际上存放的通常是基类的指针(更好的选择是智能指针)。