class Quote{
public:
Quote() = default;
Quote(const std::string &book, double sales_price):
bookNo(book), price(sales_price){}
std::string isbn() const { return bookNo;}
virtual double net_price(std::size_t n) const
{ return n*price; }
virtual ~Quote() = default;
private:
std::string bookNo;
protected:
double price = 0.0;
};
通常基类都应该定义一个虚析构函数,即使该函数不执行任何实际操作也是如此。
成员函数和继承
基类希望派生类进行覆盖的函数通常被基类定义为虚函数。基类通过在其成员函数的声明语句之前加上关键字virtual使得该函数支持动态绑定。任何构造函数之外的非静态函数都可以是虚函数。关键字virtual只能出现在类内部的声明语句之前而不能用于类外部的函数定义。如果基类把一个函数声明成虚函数,则该函数在派生类中隐式地也是虚函数。
成员函数如果没被声明成虚函数,则其解析过程发生在编译时,而非运行时。
访问控制和继承
balabala
定义派生类
class Bulk_quote : public Quote{
public:
Bulk_quote() = default;
Bulk_quote(const std::string&, double, std::size_t , double);
double net_price(std::size_t const override;
private:
std::size_t min_qty = 0;
double discount = 0.0;
};
派生类中的虚函数
C++11校准允许派生类显式地注明它使用某个成员函数覆盖它继承的虚函数
- 在形参列表后面添加一个关键字override
- 在const成员函数的const关键字后面添加一个关键字override
- 在引用成员函数的引用限定符后面添加一个关键字override
派生类对象及派生类向基类的类型转换
一个派生类对象包含多个组成部分:一个含有派生类自己定义的(非静态)成员的子对象,以及一个与该派生类继承的基类对应的子对象。
Quote item;
Bulk_quote bulk;
Quote *p = &item;
p = &bulk; //p指向bulk的Quote部分
Quote &r = bulk; //r绑定到bulk的Quote部分
在派生类对象中含有与其基类对应的组成部分。
派生类的构造函数
派生类必须使用基类的构造函数来初始化它的基类部分。(每个类控制它自己的成员初始化过程)
Bulk_quote(const std::string& book, double p,
std::size_t qty, double disc) :
Quto(book, p), min_qty(qty), discount(disc) {}
首先初始化基类的部分,然后按照声明的顺序依次初始化派生类的成员
派生类使用基类成员
派生类可以访问基类的公有成员和受保护成员:
double Bulk_quote::net_price(size_t cnt) const
{
if(cnt >= min_qty)
return cnt * (1 - discount) *price;
else
return cnt * price;
}
每个类负责定义各自的接口,要想和类的对象交互必须使用该类的接口,即使这个对象是派生类的基类部分也是如此。尽管在派生类构造函数体内给它的公有或受保护的基类成员赋值,但最好不要这么做。
继承和静态成员
如果基类定义了一个静态成员,则在整个继承体系中只存在该成员的唯一定义。静态成员遵循通用的访问控制规则,如果基类的成员是private的,则派生类无权访问它。假如是可以访问的,可以通过基类使用也可以通过派生类使用:
class Base{
public:
static void statmen();
};
class Derived : public Base{
void f(const Derived&);
};
void Derived::f(const Derived &derived_obj)
{
Base::statmen();
Derived::statmen();
derived_obj.statmen();
statmen();
}
防止继承的发生
当我们不希望其他类继承某个类时,可以在类名后加一个关键字final
class NoDerived final{ /* */ };
class Base { /* */ };
class Last final: Base { /* */ };
类型转换和继承
可以将基类的指针或引用绑定到派生类对象上。当我们使用存在继承关系的类型时,必须将一个变量或其他表达式的静态类型(static type)与该表达式对象的动态类型(dynamic type)区分开来。表达式的静态类型在编译时就是已知的,它是变量声明时的类型或表达式生成的类型;动态类型则是变量或表达式表示的内存中的对象的类型,直到运行时才可知动态类型。
Bulk_quote bulk;
Quote *itemp = &bulk; //正确:动态类型是Bulk_quote
派生类向基类的自动类型转换只对指针或引用类型有效,在派生类类型和基类类型之间不存在这种转换。
Bulk_quote bulk;
Quote item(bulk);
Quote::Quote(const Quote&);
item = bulk;
Quote::operator=(const Quote&);
当我们使用一个派生类对象为一个基类对象初始化或赋值时,只有该派生类对象中的基类部分会被拷贝、移动或赋值,它的派生类部分将被忽略。