派生类向基类转换的可访问性:
派生类向基类的转换是否可访问由使用该转换的代码决定,同时派生类的派生访问说明符也会有影响,假定D继承B:
1.只有当D公有地继承B时,用户代码才能使用派生类向基类的转换;如果D继承B的方式是私有的或者是受保护的,用户代码则不能使用该转换。
2.不论D以什么样的方式继承B,D的成员函数和右元都能使用派生类向基类的转换,派生类向其直接基类的类型转换对于派生类的成员和右元来说永远是可访问的。
3.如果D继承B的方式是公有的或者受保护的,则D的派生类的成员和右元可以使用D向B的类型转换,反之,如果D继承B的方式是私有的,则不能使用。
继承中的类作用域
派生类的作用域嵌套在其基类的作用域之内。
派生类的成员将隐藏同名的基类成员。
名字查找优先于类型检查。
构造函数与拷贝控制
虚析构含函数
继承关系对基类拷贝最直接的影响是基类通常应该定义一个虚构函数,这样就可以动态分配继承中的对象。
class Quote
{
public:
virtual ~Quote() = default; //动态绑定析构函数
如果我们删除的是一个指向派生类对象的基类指针,则需要析构函数。
};
合成拷贝与继承
定义拷贝构造或者移动构造函数
当派生类定义了拷贝或者移动操作时,该操作负责拷贝或移动包括基类部分成员在内的整个对象。
class Base {/*...*/};
class D:public Base
{
public:
//默认情况下,基类的默认构造函数初始化对象的基类部分
//要想使用拷贝或移动构造函数,我们必须在构造函数初始值列表中,显示的使用该构造函数
D(const D& d):Base(d) /*D的成员的初始值*/ {} //拷贝基类成员
D(D&& d):Base(std::move(d)) /*D的成员的初始值*/ {} //移动基类成员
};
一般来说,Base(d)一般会匹配Base的拷贝构造函数。D类型的对象d将被绑定到该构造函数的Base& 形参上,Base的拷贝构造函数将d的基类部分拷贝给要构造的函数。
在默认情况下,基类默尔构造函数初始化派生类对象的基类部分。如果我们想拷贝(移动)基类部分,则必须在派生类的构造函数初始值列表中显示的使用拷贝(移动)构造函数。
派生类赋值运算符
D &D::operator = (const D &rhs )
{
Base::operator=(rhs); //为基类部分赋值
//按照过去的方式为派生类的成员赋值
//酌情处理自赋值及释放已有资源的情况
return *this;
};
派生类析构函数
Class D:public Base
{
public:
//自动调用Base的析构函数
~D(){ 用户自定义部分 }
};
对象销毁的顺序与其创建的顺序相反,派生类析构函数首先执行,然后是基类的析构函数,沿着继承体系的顺序非反方向直至最后
如果构造函数或析构函数调用了某个虚函数,我们应该执行与构造函数或析构函数所属类型相对应的虚函数版本。
继承的构造函数
类不能继承默认、拷贝和移动构造函数。派生类继承基类构造函数的方式是提供一条注明了基类名的using声明语句。
class Bulk_quote : public Disc_qoute
{
public:
using Disc_quote::Disc_quote; //继承Disc_quote的构造函数
double net_price(std::size_t) const;
};
一个构造函数的using声明不会改变该构造函数的访问级别。
容器与继承
当派生类对象被赋值给基类对象时,其中派生类部分将被切掉,因此容器和存在继承关系的类型无法兼容。
在容器中放(智能)指针而非对象
课后题15.26、15.28、15.29
#include<iostream>
#include<string>
#include<ostream>
#include<vector>
#include<memory>
using namespace std;
class Quote
{
public:
Quote() = default;
Quote(const string& book = "", double sales_price = 0.0) :bookNo(book), price(sales_price)
{
cout << "Quote constructor is running" << endl;
}
string isbn() const
{
return bookNo;
}
virtual double net_price(size_t n) const
{
return n * price;
}
virtual void debug()
{
cout << "bookNo=" << bookNo << "price=" << price << endl;
}
virtual ~Quote()
{
cout << "Quote destructor is running" << endl;
}
friend ostream& operator<<(ostream&, Quote&);
private:
string bookNo;
protected:
double price = 0.0;
};
ostream& operator<<(ostream& os, Quote& e)
{
os << "\tUsing operator << (ostream &,Quote &);" << endl;
return os;
}
class Bulk_quote :public Quote
{
friend ostream& operator<<(ostream&, Bulk_quote&);
public:
Bulk_quote(const string& book = "", double sales_price = 0.0, size_t qty = 0, double disc = 0.0) :
Quote(book, sales_price), min_qty(qty), discount(disc)
{
cout << "Bulk_constructor is running" << endl;
}
double net_price(size_t cnt) const
{
if (cnt > min_qty)
{
return cnt * (1 - discount) * price;
}
else
{
return cnt * price;
}
}
~Bulk_quote()
{
cout << "Bulk_distructor is running" << endl;
}
private:
size_t min_qty;
double discount;
};
ostream& operator<<(ostream& os, Bulk_quote& bq)
{
os << "\tUsing operator << (ostream &,Quote &);" <<endl;
return os;
}
int main()
{
Quote base("C++ Primer", 128.0);
Bulk_quote bulk("Core Python Programming", 89, 5, 0.2);
double total_price;
double total2_price;
total_price = base.net_price(5);
total2_price = bulk.net_price(10);
cout << base <<total_price<<endl;
cout << bulk << total2_price<< endl;
vector<shared_ptr<Quote>> itemVec;
for (size_t i = 0; i != 10; ++i)
{
itemVec.push_back(make_shared<Bulk_quote>("C++ primer", 6, 5, 0.5));
}
double sum = 0;
for (auto iter = itemVec.begin(); iter != itemVec.end(); ++iter)
{
sum += (**iter).net_price(10);
}
cout << sum << endl;
return 0;
}