C++ primer 第十五章-面向对象编程

Hi!这里是山幺幺的c++ primer系列。写这个系列的初衷是,虽然在学校学习了c++,但总觉得对这门语言了解不深,因此我想来啃啃著名的c++ primer,并在这里同步记录我的学习笔记。由于我啃的是英文版,所以笔记是中英文夹杂的那种。另外由于已有一定的编程基础,所以这个系列不会含有太基础的知识,想入门的朋友最好自己啃书嘻嘻~

概述

核心思想

  • 数据抽象:define classes that separate interface from implementation
  • 继承:define classes that model the relationships among similar types
  • 动态绑定:use objects of these types while ignoring the details of how they differ

动态绑定

  • 也叫run-time binding
  • 未定义为virtual的成员函数 are resolved at compile time, not run time(即:不是动态绑定的),因为there is no question as to which function to run
  • 发生时机:dynamic binding happens when a virtual function is called through a reference (or a pointer) to a base class
double print_total(ostream &os, const Quote &item, size_t n) {
  // depending on the type of the object bound to the item parameter
  // calls either Quote::net_price or Bulk_quote::net_price
  double ret = item.net_price(n);
  os << "ISBN: " << item.isbn() // calls Quote::isbn
  << " # sold: " << n << " total due: " << ret << endl;
  return ret;
}

// basic has type Quote; bulk has type Bulk_quote
print_total(cout, basic, 20); // calls Quote version of net_price
print_total(cout, bulk, 20); // calls Bulk_quote version of net_price

多态性(polymorphism)

  • 多态的关键在于通过基类指针或引用调用一个虚函数时,编译时不确定到底调用的是基类还是派生类的函数,运行时才确定
  • 多态通过虚函数表实现:http://c.biancheng.net/view/267.html

关于子类


子类的继承列表

  • 格式:子类名+冒号+用逗号分割的(继承方式和)父类
    PS:本章只涉及single inheritance,即子类只继承自一个父类
  • 注意:inherited and derived parts of an object are not guaranteed to be stored contiguously in memory

定义子类

  • 子类必须声明 each inherited member function it intends to override
  • access specifier(包括public、private、protected) determines whether users of a derived class are allowed to know that the derived class inherits from its base class
    • public继承:public members of the base class become part of the interface of the derived class as well;we can bind an object of a publicly derived type to a pointer or reference to the base type

声明子类

class Bulk_quote : public Quote; // error: derivation list can't appear here
class Bulk_quote; // ok: right way to declare a derived class

子类的constructor

  • 子类不能直接初始化继承的成员,而必须使用基类的constructor来 initialize its base-class part
    PS:each class controls how its members are initialized
  • 初始化顺序:base class is initialized first, and then the members of the derived class are initialized in the order in which they are declared in the class
  • 栗子
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; // dynamic binding for the destructor
private:
  std::string bookNo;
protected:
  double price = 0.0; 
};

class Bulk_quote : public Quote { // Bulk_quote inherits from Quote
  Bulk_quote() = default;
  Bulk_quote(const std::string& book, double p, std::size_t qty, double disc) : Quote(book, p), min_qty(qty), discount(disc) { }
  double net_price(std::size_t) const override;
private:
  std::size_t min_qty = 0; 
  double discount = 0.0; 
};

子类对父类的访问

  • 子类可以访问基类的public and protected成员
  • the scope of a derived class is nested inside the scope of its base class,所以在子类中使用基类成员和使用子类成员的写法是相同的
double Bulk_quote::net_price(size_t cnt) const {
  if (cnt >= min_qty)
    return cnt * (1 - discount) * price;
  else
    return cnt * price;
}

关于基类


基类的静态成员

  • 若基类定义了静态成员,那么there is only one such member defined for the entire hierarchy(每个静态成员只有一个实例)
  • 栗子
class Base {
public:
  static void statmem();
};

class Derived : public Base {
  void f(const Derived&);
};

// 因为statmem是基类的public成员,所以以下代码都是合法的
void Derived::f(const Derived &derived_obj) { 
  Base::statmem(); // ok: Base defines statmem
  Derived::statmem(); // ok: Derived inherits statmem
  // ok: derived objects can be used to access static from base
  derived_obj.statmem(); // accessed through a Derived object
  statmem(); // accessed through this object
}

基类的要求

  • a class must be defined, not just declared, before we can use it as a base (所以类不能继承自己)

direct/indirect base

  • 下面的代码中,Base is a direct base to D1 and an indirect base to D2
class Base { /* ... */ } ;
class D1: public Base { /* ... */ };
class D2: public D1 { /* ... */ };
  • 子类会继承indirect base的成员(因为indirect base的成员会先被direct base继承,成为direct base的成员)

final类

  • c++ 11新特性
  • 作用:让类不能被继承
class NoDerived final { /* */ }; // NoDerived can't be a base class
class Base { /* */ };
// Last is final; we cannot inherit from Last
class Last final : Base { /* */ }; // Last can't be a base class
class Bad : NoDerived { /* */ }; // error: NoDerived is final
class Bad2 : Last { /* */ }; // error: Last is final

conversion与继承


derived-to-base conversion

  • 通过derived-to-base conversion(自动、隐式)我们可以把指向基类的内置指针/智能指针/引用绑定到子类的对象(前提是the derived-to-base conversion is accessible)
Base b; // object of base type
Derived d; // object of derived type
Base *b_ptr = &b; 
b_ptr = d; // b_ptr points to the Base part of d
Base &r = d; // r bound to the Base part of d
  • 不存在从子类对象到基类对象的隐式转换,但是可以通过从子类对象到基类指针/引用的隐式转换来把子类对象转换为基类对象,但需注意:when we initialize or assign an object of a base type from an object of a derived type, only the base-class part of the derived object is copied, moved, or assigned;the derived part of the object is ignored
Bulk_quote bulk; // object of derived type
Quote item(bulk); // uses the Quote::Quote(const Quote&) constructor
item = bulk; // calls Quote::operator=(const Quote&)

没有从基类到子类的隐式转换

  • 栗子
Quote base;
Bulk_quote* bulkP = &base; // error: can't convert base to derived
Bulk_quote& bulkRef = base; // error: can't convert base to derived
  • 即使a base pointer or reference is bound to a derived object,也没有从基类到子类的隐式转换,因为隐式转换是编译器完成的,而编译器只会通过静态类型来判断conversion是否安全
Bulk_quote bulk;
Quote *itemP = &bulk; // ok: dynamic type is Bulk_quote
Bulk_quote *bulkP = itemP; // error: can't convert base to derived
  • 但可以使用static_cast进行从基类到子类的显式转换

static type and dynamic type

  • 含义
    • static type:always known at compile time(the type with which a variable is declared or that an expression yields)
    • dynamic type:the type of the object in memory that the variable or expression represents;dynamic type may not be known until run time
  • 非指针/引用类型的对象,其静态和动态类型必然相同
  • 栗子:item的静态类型是Quote&,动态类型则要到运行时才能确定
double print_total(ostream &os, const Quote &item, size_t n) {
  // depending on the type of the object bound to the item parameter
  // calls either Quote::net_price or Bulk_quote::net_price
  double ret = item.net_price(n);
  os << "ISBN: " << item.isbn() // calls Quote::isbn
  << " # sold: " << n << " total due: " << ret << endl;
  return ret;
}

虚函数


含义

  • 基类中的虚函数含义是:希望子类自己设计这个函数
class Quote {
public:
  std::string isbn() const;
  virtual double net_price(std::size_t n) const;
};

使用要求

  • 有的函数如果不需要使用它,可以只声明不定义,但虚函数(不包括纯虚函数)必须定义,因为由于动态绑定的存在,编译器不知道which version of the virtual function is called until run time
  • 子类在定义继承自父类的虚函数时,可以加上也可以不加上关键字virtual,子类中继承自父类虚函数的函数是implicitly virtual的
  • 关键字virtual只出现在类内,类外定义成员函数的时候不要写virtual
  • classes used as the root of an inheritance hierarchy almost always define a virtual destructor
  • 除constructor外的任意非静态成员函数都可以是虚函数
  • 若子类没有 override a virtual from its base,那么子类会继承the version defined in its base class
  • 子类重写继承的虚函数时,inherited virtual function must have exactly the same parameter type(s) as the base-class function;除下面这个例外外,函数的返回类型也必须与基类中对应虚函数的返回类型相同:
    • 当返回类型是a reference (or pointer) to types that are themselves related by inheritance时,比如:if D is derived from B, then a base class virtual can return a B* and the version in the derived can return a D*(前提是the derived-to-base conversion from D to B is accessible)

override

  • c++ 11新特性
  • 子类中可以写上关键字override,来explicitly note that it intends a member function to override a virtual that it inherits
  • 作用:子类可以定义与基类中虚函数同名但parameter list不同的函数,编译器considers such a function to be independent from the base-class function(即the derived version does not override the version in the base class);但这通常是bug,程序员本意是想override,所以,通过写明override,就可以让编译器查是不是真的override了
  • 栗子
struct B {
  virtual void f1(int) const;
  virtual void f2();
  void f3();
};

struct D1 : B {
  void f1(int) const override; // ok: f1 matches f1 in the base
  void f2(int) override; // error: B has no f2(int) function
  void f3() override; // error: f3 not virtual
  void f4() override; // error: B doesn't have a function named f4
};

final函数

  • 作用:让函数不可以被override
struct D2 : B {
  // inherits f2() and f3() from B and overrides f1(int)
  void f1(int) const final; // subsequent classes can't override f1(int)
};
struct D3 : D2 {
  void f2(); // ok: overrides f2 inherited from the indirect base, B
  void f1(int) const; // error: D2 declared f2 as final
};

绑定时间

  • When we call a virtual function on an expression that has a plain—nonreference and nonpointer—type, that call is bound at compile time:下面代码中,我们可以改变 the value (i.e., the contents) of the object that base represents, 但我们不能改变 the type of that object,所以在编译时即可绑定
base = derived; // copies the Quote part of derived into base
base.net_price(20); // calls Quote::net_price
  • When we call a virtual function on an expression that has a reference or pointer type, that call is bound at run time

PS:calls to nonvirtual functions are bound at compile time

默认参数

  • If a call uses a default argument, the value that is used is the one defined by the static type through which the function is called
  • 使用建议:有默认参数的虚函数 should use the same argument values in the base and derived classes

使用指定版本的虚函数

  • 栗子
// calls the version from the base class regardless of the dynamic type of baseP
double undiscounted = baseP->Quote::net_price(42);
  • 这种函数调用 will be resolved at compile time

纯虚函数


使用要求

  • 与普通虚函数不同,纯虚函数不一定需要定义
  • = 0 may appear only on the declaration of a virtual function in the class body;纯虚函数的定义必须写在类外
  • 栗子:net_price是纯虚函数
class Disc_quote : public Quote {
public:
  Disc_quote() = default;
  Disc_quote(const std::string& book, double price, std::size_t qty, double disc):
  Quote(book, price), quantity(qty), discount(disc) { }
  double net_price(std::size_t) const = 0;
protected:
  std::size_t quantity = 0; // purchase size for the discount to apply
  double discount = 0.0; // fractional discount to apply
};

抽象基类


定义

  • a class containing (or inheriting without overridding) a pure virtual function is 抽象基类

使用要求

  • 不能直接创建抽象基类类型的对象

访问控制


原则

  • each class controls access to its own members

protected成员

  • inaccessible to general users of the class
  • accessible to members and friends of classes directly derived from this class(注意:子类的 member or friend may access the protected members of the base class only through a derived object)(相当于继承后,变成了子类的private成员)
class Base {
protected:
  int prot_mem; // protected member
};

class Sneaky : public Base {
  friend void clobber(Sneaky&); // can access Sneaky::prot_mem
  friend void clobber(Base&); // can't access Base::prot_mem
  int j; // j is private by default
};
  // ok: clobber can access the private and protected members in Sneaky objects
  void clobber(Sneaky &s) { s.j = s.prot_mem = 0; }
  // error: clobber can't access the protected members in Base
  void clobber(Base &b) { b.prot_mem = 0; }

继承中的访问控制

  • 继承中的访问控制不影响子类对直接基类(准确地说是子类中继承自直接基类的成员)的访问权限:private继承和public继承都可以访问直接基类的protected成员
class Base {
public:
  void pub_mem(); // public member
protected:
  int prot_mem; // protected member
private:
  char priv_mem; // private member
};

struct Pub_Derv : public Base {
  // ok: derived classes can access protected members
  int f() { return prot_mem; }
  // error: private members are inaccessible to derived classes
  char g() { return priv_mem; }
};

struct Priv_Derv : private Base {
  // private derivation doesn't affect access in the derived class
  int f1() const { return prot_mem; }
};
  • 继承中的访问控制影响子类的使用者(包括other classes derived from the derived class)对其直接基类(准确地说是子类中继承自直接基类的成员)的访问权限
Pub_Derv d1; // members inherited from Base are public
Priv_Derv d2; // members inherited from Base are private
d1.pub_mem(); // ok: pub_mem is public in the derived class
d2.pub_mem(); // error: pub_mem is private in the derived class

struct Derived_from_Public : public Pub_Derv {
  // ok: Base::prot_mem remains protected in Pub_Derv
  int use_base() { return prot_mem; }
};
struct Derived_from_Private : public Priv_Derv {
  // error: Base::prot_mem is private in Priv_Derv
  int use_base() { return prot_mem; }
};
  • 一些总结
    • public继承:父类中的public、protected和private属性在子类中不发生改变
    • protected继承:父类中的public属性在子类中变为protected,父类中的protected和private属性在子类中不变
    • private继承:父类中的三种访问属性在子类中都会变成private

derived-to-base conversion的权限控制

  • for any given point in your code, if a public member of the base class would be accessible, then the derived-to-base conversion is also accessible, and not otherwise,具体来说:(假设D直接继承B)
    • user code may use the derived-to-base conversion only if D inherits publicly from B
    • D的成员和友元can use the conversion to 直接基类B
    • D的子类的成员和友元can use the conversion to B if D inherits from B using either public or protected

修改访问权限

  • 方法:使用using,本来size和n都是private,using后分别变为了public和protected
class Base {
public:
  std::size_t size() const { return n; }
protected:
  std::size_t n;
};

class Derived : private Base { // note: private inheritance
public:
  // maintain access levels for members related to the size of the object
  using Base::size;
protected:
  using Base::n;
};
  • 注意:只能对accessible的name用using(比如上面代码中,Derived就不能对Base的private成员用using)

默认的继承方式

  • class默认为private继承,struct默认为public继承
struct D1 : Base { /* ... */ }; // public inheritance by default
class D2 : Base { /* ... */ }; // private inheritance by default

PS:struct和class就只有默认继承方式和默认成员是public/private这两个区别

继承中的Class Scope


概述

  • the scope of a derived class is nested inside the scope of its base classes,即:If a name is unresolved within the scope of the derived class, the enclosing base-class scopes are searched for a definition of that name
  • name lookup happens at compile time,因为static type of an object, reference, or pointer determines which members of that object are visible
class Bulk_quote : public Quote {
public:
  std::pair discount_policy() const { return {quantity, discount}; }
};

Bulk_quote bulk;
Bulk_quote *bulkP = &bulk; // static and dynamic types are the same
Quote *itemP = &bulk; // static and dynamic types differ
bulkP->discount_policy(); // ok: bulkP has type Bulk_quote*
itemP->discount_policy(); // error: itemP has type Quote*

PS:因为itemP是指向Quote的指针,所以name lookup是从Quote开始往外层走的,不会搜到Bulk_quote

name collision

  • names defined in an inner scope (e.g., a derived class) hide uses of that name in the outer scope (e.g., a base class),even if the functions have different parameter lists
struct Base {
  Base(): mem(0) { }
protected:
  int mem;
};
struct Derived : Base {
  Derived(int i): mem(i) { } // initializes Derived::mem to i
  // Base::mem is default initialized
  int get_mem() { return mem; } // returns Derived::mem
protected:
  int mem; // hides mem in the base
};

struct Base {
  int memfcn();
};
struct Derived : Base {
  int memfcn(int); // hides memfcn in the base
};
Derived d; Base b;
b.memfcn(); // calls Base::memfcn
d.memfcn(10); // calls Derived::memfcn
d.memfcn(); // error: memfcn with no arguments is hidden:是覆盖不是重载
d.Base::memfcn(); // ok: calls Base::memfcn

用::来使用被隐藏的name

struct Derived : Base {
  int get_base_mem() { return Base::mem; }
};

name lookup的步骤总结

p->mem() (or obj.mem())对应如下步骤

  1. look for mem in the class that corresponds to the static type of p (or obj),若没找到,则沿着继承链继续找
  2. 找到mem后,做normal type checking,确定是合法的
  3. 编译器生成代码
  • 若mem是虚函数,且p/obj是指针/引用,那么the compiler generates code to determine at run time which version to run based on the dynamic type of the object
  • 否则,the compiler generates a normal function call

补充:重载、重写与隐藏


区别

  • 表格总结
  • 当参数列表不同时,无论基类中的函数是否被virtual修饰,基类函数都是被隐藏,而不是被重写;参数相同时,若基类函数没有关键字virtual,此时基类函数被隐藏,若有则被重写

栗子

  • 注意virtual functions must have the same parameter list in the base and derived classes,才能被重写
  • dynamic type doesn’t matter when we call a nonvirtual function
class Base {
public:
  virtual int fcn();
};

class D1 : public Base {
public:
  // hides fcn in the base; this fcn is not virtual
  // D1 inherits the definition of Base::fcn()
  int fcn(int); // parameter list differs from fcn in Base
  virtual void f2(); // new virtual function that does not exist in Base
};

class D2 : public D1 {
public:
  int fcn(int); // nonvirtual function hides D1::fcn(int)
  int fcn(); // overrides virtual fcn from Base
  void f2(); // overrides virtual f2 from D1
};

Base bobj; D1 d1obj; D2 d2obj;
Base *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj;
bp1->fcn(); // virtual call, will call Base::fcn at run time
bp2->fcn(); // virtual call, will call Base::fcn at run time
bp3->fcn(); // virtual call, will call D2::fcn at run time
D1 *d1p = &d1obj; D2 *d2p = &d2obj;
bp2->f2(); // error: Base has no member named f2
d1p->f2(); // virtual call, will call D1::f2() at run time
d2p->f2(); // virtual call, will call D2::f2() at run time

// dynamic type doesn’t matter when we call a nonvirtual function
Base *p1 = &d2obj; D1 *p2 = &d2obj; D2 *p3 = &d2obj;
p1->fcn(42); // error: Base has no version of fcn that takes an int
p2->fcn(42); // statically bound, calls D1::fcn(int)
p3->fcn(42); // statically bound, calls D2::fcn(int)

重写重载函数

  • 基类有多个重载函数时,若子类想要继承all of them,则must override all of them(全部重写,重写一部分会导致基类函数被隐藏) or none of them(直接继承),而不能只重写其中的一部分;除非使用using:
    • a using declaration for a base-class member function adds all the overloaded instances of that function to the scope of the derived class,之后就可以define only those functions that truly depend on 子类,并使用the inherited definitions for the others
    • a using declaration specifies only a name; it may not specify a parameter list
    • 注意:只能对accessible的name用using,可以在子类的public/protected/private下使用using(与前文提到的using的使用规则相同)

Constructor与拷贝控制的继承


基类的析构函数

  • 基类的析构函数一般需要是virtual的,这样objects in the inheritance hierarchy 才能 be dynamically allocated;比如:if we delete a pointer of type Derived*, that pointer might point at a Base object,是virtual才能运行正确的析构函数
// Bulk_quote继承自Quote 
Quote *itemP = new Quote; // same static and dynamic type
delete itemP; // destructor for Quote called
itemP = new Bulk_quote; // static and dynamic types differ
delete itemP; // destructor for Bulk_quote called
  • 像其他成员一样,析构函数的virtual性质是会继承给子类的,不管子类的析构函数是自定义的还是合成的,都是virtual的

基类的deleted对子类synthesized成员的影响

  • 若基类的 default constructor, copy constructor, copy-assignment operator, or destructor in the base class is deleted or inaccessible,那么the corresponding synthesized member in the derived class is deleted:因为base-class part of 子类无法做相应处理
  • 若基类的析构函数inaccessible or deleted,那么the synthesized default and copy constructors in the derived classes are defined as deleted:因为base-class part of 子类无法析构
  • 若基类的析构函数 deleted or inaccessible,那么子类的synthesized move constructor是deleted;若基类的move assign/constructor is deleted or inaccessible,那么子类不会合成corresponding operation,若用=default要求合成一个,合成的是deleted
class B {
public:
  B();
  B(const B&) = delete;
  // other members, not including a move constructor
};
class D : public B {
  // no constructors
};
D d; // ok: D's synthesized default constructor uses B's default constructor
D d2(d); // error: D's synthesized copy constructor is deleted
D d3(std::move(d)); // error: implicitly uses D's deleted copy constructor

子类的synthesized成员

  • 子类的synthesized成员会调用基类的默认成员来处理子类的base part
  • 若基类无 move operations,那么子类也没有 synthesized move operations;若基类有 move operations,那么子类会有 synthesized move operations

子类的构造与拷贝控制

  • 子类的 copy or move operation is responsible for copying or moving the entire object, including base-class members;子类的析构函数则只需要管非base-class的成员
  • 子类的Copy or Move Constructor:
    • 需显示调用corresponding base-class constructor 来处理 base part
    • 正确的栗子:以复制构造函数为例,D的复制构造函数必须用Base(d),这样才会调用Base(const Base&)复制base part,否则就会像下面错误的栗子里一样,base part被B的默认构造函数初始化
    class Base { /* ... */ } ;
    class D: public Base {
    public:
      // to use the copy or move constructor, we must explicitly call that
      // constructor in the constructor initializer list
      D(const D& d): Base(d) // copy the base members
      /* initializers for members of D */ { /* ... */ }
      D(D&& d): Base(std::move(d)) // move the base members
      /* initializers for members of D */ { /* ... */ }
    };
    
    • 错误的栗子
    // base-class part is default initialized, not copied
    D(const D& d) /* member initializers, but no base-class initializer */
    { /* ... */ }
    
  • 子类的Assignment Operator
    • 需显示调用基类的 assignment operator 来处理 base part
    • 正确的栗子
    // Base::operator=(const Base&) is not invoked automatically
    D &D::operator=(const D &rhs) {
      Base::operator=(rhs); // assigns the base part
      // assign the members in the derived class, as usual,
      // handling self-assignment and freeing existing resources as appropriate
      return *this;
    }
    
  • 子类的析构函数
    • responsible only for destroying the resources allocated by the derived class:因为base-class parts of an object are implicitly destroyed
    • 栗子
    class D: public Base {
    public:
      // Base::~Base invoked automatically
      ~D() { /* do what it takes to clean up derived members */ }
    };
    

在constructor or destructor中调用虚函数

  • 当子类的constructor or destructor调用基类的constructor or destructor处理它的base part时,编译器会把它当作一个基类的对象,所以若 constructor or destructor calls a virtual, the version that is run is the one corresponding to the type of the constructor or destructor itself,即会运行基类版本的virtual

子类对构造与拷贝控制的继承

  • 子类不能继承 default, copy, and move constructors,若子类中未定义它们,则按照synthesize的规则合成
  • 子类继承constructors的方法:using,编译器会为子类生成形如derived(parms) : base(args) { }的constructors,子类自己的成员会被默认初始化
class Bulk_quote : public Disc_quote {
public:
  using Disc_quote::Disc_quote; // inherit Disc_quote's constructors
  double net_price(std::size_t) const;
};
  • 使用using继承的注意事项
    • 这里的using不管写在哪都不会改变constructor的访问权限性质,即在基类中是public/protected/private的在子类中还是public/protected/private
    • using不能使用explicit or constexpr,若基类中的constructor是explicit or constexpr,子类会自动继承这个属性
    • 若基类constructor有默认参数,则子类gets multiple inherited constructors in which each parameter with a default argument is successively omitted,比如:若基类constructor有两个形参,第二个有默认值,则子类会继承到两个constructor,其一 with both parameters (and no default argument),其二 with a single parameter corresponding to the left-most, non-defaulted parameter in the base class
    • 若基类有多个constructors,那么除了下面两个例外外,子类会 inherit each of the constructors from its base class
      1. 若子类 defines a constructor with the same parameters as a constructor in the base, then that constructor is not inherited;其他 constructors 仍然被继承
      2. default, copy, and move constructors are not inherited 而是被 synthesized using the normal rules;因为继承的constructor不被视作 user-defined constructor,所以只有inherited constructors的子类 will have synthesized constructors

容器与继承


如何把基类与子类放在同一容器中

  • 不要直接把对象放在容器中:因为这样就只能用基类类型的容器,会导致子类对象只有base part被保留
  • Put (Smart) Pointers, Not Objects, in Containers
// Bulk_quote继承自Quote
vector> basket;
basket.push_back(make_shared("0-201-82470-1", 50));
basket.push_back(make_shared("0-201-54848-8", 50, 10, .25));
// calls the version defined by Quote
cout << basket.back()->net_price(15) << endl;

你可能感兴趣的:(C++ primer 第十五章-面向对象编程)