C++ primer 第十八章-大型程序工具

Hi蛾默!這里是山幺幺的c++ primer系列捷绒。寫這個系列的初衷是瑰排,雖然在學校學習了c++,但總覺得對這門語言了解不深疙驾,因此我想來啃啃著名的c++ primer凶伙,并在這里同步記錄我的學習筆記。由于我啃的是英文版它碎,所以筆記是中英文夾雜的那種尚揣。另外由于已有一定的編程基礎丙挽,所以這個系列不會含有太基礎的知識警检,想入門的朋友最好自己啃書嘻嘻~

異常處理


概述

  • 如何選handler:the one nearest in the call chain that matches the type of the thrown object

stack unwinding(棧展開)

  • 含義:拋出異常時统求,將暫停當前函數(shù)的執(zhí)行,開始查找匹配的catch子句挖息。首先檢查throw本身是否在try塊內(nèi)部金拒,如果是,檢查與該try相關的catch子句套腹,看是否可以處理該異常绪抛。如果不能處理,就退出當前函數(shù)电禀,并且釋放當前函數(shù)的內(nèi)存并銷毀局部對象(class會調(diào)用析構(gòu)函數(shù)幢码,內(nèi)置類型無需編譯器處理即會銷毀),繼續(xù)到上層的調(diào)用函數(shù)中查找尖飞,直到找到一個可以處理該異常的catch或the main function itself is exited症副。這個過程稱為棧展開(stack unwinding)。當處理該異常的catch結(jié)束之后政基,緊接著該catch之后的點繼續(xù)執(zhí)行贞铣。
  • once an exception is raised, it cannot remain unhandled. If no matching catch is found, the program calls the library terminate function which stops execution of the program
  • 如果一個塊通過new動態(tài)分配內(nèi)存,并且在釋放該資源之前發(fā)生異常沮明,該塊因異常而退出辕坝,那么在棧展開期間不會釋放該資源,編譯器不會刪除該指針荐健,這樣就會造成內(nèi)存泄露
  • During stack unwinding, an exception has been raised but is not yet handled. If a new exception is thrown during stack unwinding and not caught in the function that threw it, terminate is called. Because destructors may be invoked during stack unwinding, they should never throw exceptions that the destructor itself does not handle
  • all of the standard library types guarantee that their destructors will not raise an exception

異常對象和throw expression

  • compiler uses the thrown expression to copy initialize an exception object酱畅,所以the expression in a throw
    1. 必須是complete type(即聲明且定義了)
    2. 若是類,則必須有an accessible destructor and an accessible copy or move constructor
    3. 若是array/函數(shù)類型摧扇,它會被converted to its corresponding pointer type
  • 異常對象are in space, managed by the compiler, that is guaranteed to be accessible to whatever catch is invoked
  • 異常對象 is destroyed after the exception is completely handled
  • it is almost certainly an error to throw 一個指向局部變量的指針,因為throwing a pointer requires that the object to which the pointer points exist wherever the corresponding handler resides
  • 異常對象的類型是編譯時就確定的挚歧,和throw expression的靜態(tài)類型相同扛稽,所以If a throw expression dereferences a pointer to a base-class type, and that pointer points to a derived-type object, then the thrown object is sliced down,即only the base-class part is thrown

catch exception

  • catch的形參必須是complete type且不能是右值引用滑负;對應的實參是異常對象
  • catch的形參與實參的匹配非常嚴格在张,只允許以下幾種conversion
    1. from nonconst to const:即a throw of a nonconst object can match a catch specified to take a reference to const
    2. from derived type to base type
    3. an array is converted to a pointer to the type of the array; a function is converted to the appropriate pointer to function type
  • catch-all handlers:match所有類型的異常用含,通常與rethrow合用,先does whatever local work can be done 然后 rethrows the exception
void manip() {
  try {
    // actions that cause an exception to be thrown
  }
  catch (...) {
    // work to partially handle the exception
    throw;
  }
}

rethrow

  • 定義:a throw that is not followed by an expression
  • 作用:does not specify an expression; the (current) exception object is passed up the chain
  • 使用要求:can appear only in a catch or in a function called (directly or indirectly) from a catch
  • If an empty throw is encountered when a handler is not active, terminate is called
  • 栗子
catch (my_error &eObj) { // specifier is a reference type
  eObj.status = errCodes::severeErr; // modifies the exception object
  throw; // the status member of the exception object is severeErr
} catch (other_error eObj) { // specifier is a nonreference type
  eObj.status = errCodes::badErr; // modifies the local copy only
  throw; // the status member of the exception object is unchanged
}

function try block

  • 使用場景:處理constructor initializer時異嘲镓遥可能出現(xiàn)啄骇,而constructor的函數(shù)體還未執(zhí)行,所以constructor函數(shù)體中的catch語句不能catch該異常瘟斜,所以要用function try block
  • 定義associate a group of catch clauses with the initialization phase of a constructor (or the destruction phase of a destructor) as well as with the constructor’s (or destructor’s) function body
  • 栗子:注意下面代碼中的catch可以handle exceptions thrown either from within the member initialization list or from within the constructor body
template <typename T>
Blob<T>::Blob(std::initializer_list<T> il) try : data(std::make_shared<std::vector<T>>(il)) {
  /* empty body */
} catch (const std::bad_alloc &e) { 
  handle_out_of_memory(e); 
}
  • 特別注意:function try block只處理構(gòu)造函數(shù)開始執(zhí)行后拋出的異常缸夹,所以初始化構(gòu)造函數(shù)的形參時拋出的異常不屬于function try block;與其他函數(shù)調(diào)用一樣螺句,初始化形參時拋出的異常屬于調(diào)用函數(shù)的那句話所在的context

noexcept

  • 是c++ 11新特性
  • noexcept specifier is not part of a function’s type
  • 栗子
void recoup(int) noexcept; // won't throw
void recoup(int) throw(); // 舊標準下的equivalent declaration
void alloc(int); // might throw

void recoup(int) noexcept(true); // recoup won't throw
void alloc(int) noexcept(false); // alloc can throw
void f() noexcept(noexcept(g())); // f has same exception specifier as g
  • 要求:關鍵字noexcept must appear on all of the declarations and the corresponding definition of a function or on none of them虽惭;關鍵字noexcept需寫在trailing return之前;我們可以specify noexcept on the declaration and definition of a function pointer蛇尚;關鍵字noexcept不能出現(xiàn)在typedef or type alias中芽唇;在成員函數(shù)中,關鍵字noexcept follows any const or reference qualifiers, and it precedes final, override, or = 0 on a virtual function
  • 作用:promises the callers of the nonthrowing function that they will never need to deal with exceptions
  • 標注了noexcept的函數(shù)可能會拋出異常取劫,因為編譯器不檢查這個性質(zhì)匆笤;If a noexcept function does throw, terminate is called, thereby enforcing the promise not to throw at run time
// this function will compile, even though it clearly violates its exception specification
void f() noexcept { // promises not to throw any exception
  throw exception(); // violates the exception specification
}
  • 指向函數(shù)的指針與函數(shù)的匹配:聲明為noexcept的函數(shù)指針只能指向noexcept的函數(shù);而a pointer that specifies (explicitly or implicitly) that it might throw can point to any function
// both recoup and pf1 promise not to throw
void (*pf1)(int) noexcept = recoup;
// ok: recoup won't throw; it doesn't matter that pf2 might
void (*pf2)(int) = recoup;
pf1 = alloc; // error: alloc might throw but pf1 said it wouldn't
pf2 = alloc; // ok: both pf2 and alloc might throw
  • 虛函數(shù)的noexcept性質(zhì):若基類虛函數(shù)是noexcept谱邪,則子類對應的虛函數(shù)必須是noexcept炮捧;若基類虛函數(shù)沒有聲明noexcept,則子類對應的虛函數(shù)可以是noexcept也可以不是
class Base {
public:
  virtual double f1(double) noexcept; // doesn't throw
  virtual int f2() noexcept(false); // can throw
  virtual void f3(); // can throw
};
class Derived : public Base {
public:
  double f1(double); // error: Base::f1 promises not to throw
  int f2() noexcept(false); // ok: same specification as Base::f2
  void f3() noexcept; // ok: Derived f3 is more restrictive
};
  • synthesized member的noexcept性質(zhì):
    1. If all the corresponding operation for all the members and base classes promise not to throw, then the synthesized member is noexcept
    2. If any function invoked by the synthesized member can throw, then the synthesized member is noexcept(false)
    3. if we do not provide an exception specification for a destructor that we do define, the compiler synthesizes one for us

standard-library exception classes

  • inheritance hierarchy一覽
  • 大家都定義了:copy constructor虾标、copy-assignment operator寓盗、虛析構(gòu)函數(shù)、a virtual member function named what(返回const char*璧函,指向a null-terminated character array傀蚌;guaranteed not to throw any exceptions)
  • exception, bad_cast, and bad_alloc:還定義了default constructor
  • runtime_error and logic_error:還定義了constructors that take a C-style character string or a library string argument;what函數(shù)returns the message used to initialize the exception object
  • 栗子
// hypothetical exception classes for a bookstore application
class out_of_stock: public std::runtime_error {
public:
  explicit out_of_stock(const std::string &s):
  std::runtime_error(s) { }
};
class isbn_mismatch: public std::logic_error {
public:
  explicit isbn_mismatch(const std::string &s):
  std::logic_error(s) { }
  isbn_mismatch(const std::string &s,
  const std::string &lhs, const std::string &rhs):
  std::logic_error(s), left(lhs), right(rhs) { }
  const std::string left, right;
};

// throws an exception if both objects do not refer to the same book
Sales_data& Sales_data::operator+=(const Sales_data& rhs) {
  if (isbn() != rhs.isbn())
    throw isbn_mismatch("wrong isbns", isbn(), rhs.isbn());
  units_sold += rhs.units_sold;
  revenue += rhs.revenue;
  return *this;
}

// use the hypothetical bookstore exceptions
Sales_data item1, item2, sum;
while (cin >> item1 >> item2) { // read two transactions
  try {
    sum = item1 + item2; // calculate their sum
    // use sum
  } catch (const isbn_mismatch &e) {
    cerr << e.what() << ": left isbn(" << e.left
    << ") right isbn(" << e.right << ")" << endl;
  }
}

命名空間


定義命名空間

  • 命名空間中可以定義的內(nèi)容:classes, variables (with their initializations), functions (with their definitions), templates, and other namespaces
  • 要求與性質(zhì)
    • a namespace name must be unique within the scope in which the namespace is defined
    • namespaces may be defined at global scope or inside another namespace
    • namespaces may not be defined inside a function or a class
    • different namespaces may have members with the same name
    • names defined in a namespace may be accessed directly by other members of the namespace, including scopes nested within those members
    • 命名空間的定義可以是不連續(xù)的蘸吓,所以一段命名空間的定義可能是新建命名空間善炫,也可能adds to an existing one
    • 和類一樣,成員在命名空間內(nèi)聲明了之后库继,才能在命名空間外定義箩艺,且定義時需標明命名空間::
    • 和類一樣,我們通常separate interface and implementation of namespaces:頭文件中放類的定義宪萄、函數(shù)/對象的聲明艺谆,source file中放其他成員的定義
    // ---- Sales_data.h----
    // #includes should appear before opening the namespace
    #include <string>
    namespace cplusplus_primer {
    class Sales_data { /* ... */};
    Sales_data operator+(const Sales_data&, const Sales_data&);
    // declarations for the remaining functions in the Sales_data interface
    }
    
    // ---- Sales_data.cc----
    // be sure any #includes appear before opening the namespace
    #include "Sales_data.h"
    namespace cplusplus_primer {
    // definitions for Sales_data members and overloaded operators
    }
    
  • 栗子
namespace cplusplus_primer {
class Sales_data { / * ... * /};
Sales_data operator+(const Sales_data&, const Sales_data&);
class Query { /* ... */ };
class Query_base { /* ... */};
} // like blocks, namespaces do not end with a semicolon

// namespace members defined outside the namespace must use qualified names
cplusplus_primer::Sales_data cplusplus_primer::operator+(const Sales_data& lhs, const Sales_data& rhs) {
  Sales_data ret(lhs); // 已經(jīng)在cplusplus_primer空間中了,所以不用::
  // ...
}

nested namespace

  • nested namespace中的名字可以與外層namespace中的名字重復拜英,nested namespace中的名字會hide外層名字

global namespace

  • ::member_name refers to a member of the global namespace

inline namespace

  • c++ 11新特性
  • 作用:names in an inline namespace can be used as if they were direct members of the enclosing namespace
  • 栗子:因為FifthEd is inline静汤,所以code that refers to cplusplus_primer:: will get the
    version from that namespace,而以前版本的code也可以獲得,比如cplusplus_primer::FourthEd::Query_base
inline namespace FifthEd {
// namespace for the code from the Primer Fifth Edition
}
namespace FifthEd { // implicitly inline虫给,可以寫/不寫關鍵字inline
class Query_base { ... };
// other Query-related declarations
}

namespace FourthEd {
class Item_base { /* ... */};
class Query_base { /* ... */};
// other code from the Fourth Edition
}

namespace cplusplus_primer {
#include "FifthEd.h"
#include "FourthEd.h"
}

unnamed namespace

  • 其中定義的變量有static lifetime:created before their first use and destroyed when the program ends
  • each file has its own unnamed namespace藤抡;不同文件的unnamed namespace不相關
  • If a header defines an unnamed namespace, the names in that namespace define different entities local to each file that includes the header
    PS:在unnamed namespace出現(xiàn)之前,c++從c語言繼承了這種方法:programs declared names as static to make them local to a file
  • names defined in an unnamed namespace are in the same scope as the scope at which the namespace is defined抹估,變量等的名字不能重復:
int i; // global declaration for i
namespace {
int i;
}
// ambiguous: defined globally and in an unnested, unnamed namespace
i = 10;

namespace local {
namespace {
int i;
}
}
// ok: i defined in a nested unnamed namespace is distinct from global i
local::i = 42;

namespace alias

namespace primer = cplusplus_primer;
namespace Qlib = cplusplus_primer::QueryLib;

PS:一個namespace可以有多個別名

using

  • using declaration
    • 作用:make一個成員visible
    • 可以用在:global / local / namespace / class scope(注意:在class scope中缠黍,using declaration只能refer to base class members)
    • visible from the point of the using declaration to the end of the scope in which the declaration appears
    • entities with the same name defined in an outer scope are hidden
  • using directive
    • 作用:make all the names from a specific namespace visible
    • 可以用在:global / local / namespace scope
    • visible from the point of the using directiveto the end of the scope in which the directive appears
  • using declaration 與 using directive 的區(qū)別
    • using declaration puts the name in the same scope as that of the using declaration itself
    • using directive lifts the namespace members into the nearest scope that contains both the namespace itself and the using directive(因為a namespace might include definitions that cannot appear in a local scope)
  • using directive的栗子一
// namespace A and function f are defined at global scope
namespace A {
int i, j;
}
void f() {
  using namespace A; // injects the names from A into the global scope
  cout << i * j << endl; // uses i and j from namespace A
  // ...
}
  • using directive的栗子二:members of blip appear as if they were defined in the scope in which both blip and manip are defined
namespace blip {
int i = 16, j = 15, k = 23;
// other declarations
}
int j = 0; // ok: j inside blip is hidden inside a namespace
void manip() {
  // using directive; the names in blip are ''added'' to the global scope
  using namespace blip; // clash between ::j and blip::j
  // detected only if j is used
  ++i; // sets blip::i to 17
  ++j; // error ambiguous: global j or blip::j?
  ++::j; // ok: sets global j to 1
  ++blip::j; // ok: sets blip::j to 16
  int k = 97; // local k hides blip::k
  ++k; // sets local k to 98
}

name lookup

  • 栗子一
namespace A {
  int i;
  namespace B {
    int i; // hides A::i within B
    int j;
    int f1() {
      int j; // j is local to f1 and hides A::B::j
      return i; // returns B::i
    }
  } // namespace B is closed and names in it are no longer visible
  int f2() {
    return j; // error: j is not defined
  }
  int j = i; // initialized from A::i
}
  • 栗子二:A::C1::f3 indicate the reverse order in which the class scopes and namespace scopes are to be searched
namespace A {
  int i;
  int k;
  class C1 {
  public:
    C1(): i(0), j(0) { } // ok: initializes C1::i and C1::j
    int f1() { return k; } // returns A::k
    int f2() { return h; } // error: h is not defined
    int f3();
  private:
    int i; // hides A::i within C1
    int j;
  };
  int h = i; // initialized from A::i
}
// member f3 is defined outside class C1 and outside namespace A
int A::C1::f3() { return h; } // ok: returns A::h

friend

  • when a class declares a friend, the friend declaration does not make the friend visible,但如果該友元函數(shù)的形參是an otherwise undeclared class or function药蜻,那么該友元函數(shù)和形參中定義的name都會被認為是 member of the closest enclosing namespace
namespace A {
class C {
  // two friends; neither is declared apart from a friend declaration
  // these functions implicitly are members of namespace A
  friend void f2(); // won't be found, unless otherwise declared
  friend void f(const C&); // found by argument-dependent lookup
};
}

int main() {
  A::C cobj;
  f(cobj); // ok: finds A::f through the friend declaration in A::C
  f2(); // error: A::f2 not declared
}

overloading

  • 若函數(shù)調(diào)用時瓷式,形參是一個類(設為A),那么在找重載函數(shù)的candidate時谷暮,也會到定義了A蒿往、A的基類的命名空間找candidate函數(shù)
namespace NS {
class Quote { /* ... */ };
  void display(const Quote&) { /* ... */ }
}
// Bulk_item's base class is declared in namespace NS
class Bulk_item : public NS::Quote { /* ... */ };
int main() {
  Bulk_item book1;
  display(book1); // NS::display也是candidate
  return 0;
}
  • using declaration與重載
    • when we write a using declaration for a function, all the versions of that function are brought into the current scope
    using NS::print(int); // error: cannot specify a parameter list
    using NS::print; // ok: using declarations specify names only
    
    • 若在local scope中寫using declaration,則它會hide existing declarations for that name in the outer scope
    • 若using declaration introduces 某函數(shù)湿弦,它和當前scope(即using declaration所在的scope)中的一個函數(shù)的名字和參數(shù)列表相同瓤漏,則using declaration is in error
    • 其他情況下,using declaration會增加重載函數(shù)的candidate
  • using directive與重載
namespace AW {
int print(int);
}
namespace Primer {
double print(double);
}
// using directives create an overload set of functions from different namespaces
using namespace AW;
using namespace Primer;
long double print(long double);
int main() {
  print(1); // calls AW::print(int)
  print(3.1); // calls Primer::print(double)
  return 0;
}
  • 若using directive introduces a function that has the same parameters as an existing function颊埃,不會報錯蔬充,除非we try to call the function without specifying the version
  • 其他情況下,using directive會增加重載函數(shù)的candidate

Multiple Inheritance


概述

  • 含義:inherits the properties of all its parents
  • a base class may appear only once in a given derivation list
  • 圖例
class Bear : public ZooAnimal { /* ... */ };
class Panda : public Bear, public Endangered { /* ... */ };

初始化與constructors

  • derived constructors initialize all base classes班利,且初始化順序與class derivation list中的順序一致
// explicitly initialize both base classes
Panda::Panda(std::string name, bool onExhibit)
: Bear(name, onExhibit, "Panda"), Endangered(Endangered::critical) { }
// implicitly uses the Bear default constructor to initialize the Bear subobject
Panda::Panda()
: Endangered(Endangered::critical) { }
  • 子類可以從多個基類繼承constructor饥漫,但不能從多個基類繼承多個形參列表相同的constructors
    • 錯誤的栗子
    struct Base1 {
      Base1() = default;
      Base1(const std::string&);
      Base1(std::shared_ptr<int>);
    };
    struct Base2 {
      Base2() = default;
      Base2(const std::string&);
      Base2(int);
    };
    // error: D1 attempts to inherit D1::D1 (const string&) from both base classes
    struct D1: public Base1, public Base2 {
      using Base1::Base1; // inherit constructors from Base1
      using Base2::Base2; // inherit constructors from Base2
    };
    
    • 正確的栗子
    struct D2: public Base1, public Base2 {
      using Base1::Base1; // inherit constructors from Base1
      using Base2::Base2; // inherit constructors from Base2
      // D2 must define its own constructor that takes a string
      D2(const string &s): Base1(s), Base2(s) { }
      D2() = default; // needed once D2 defines its own constructor
    };
    

析構(gòu)函數(shù)

  • 子類的析構(gòu)函數(shù)只負責cleaning up resources allocated by 子類
  • 合成的析構(gòu)函數(shù)函數(shù)體為空
  • 析構(gòu)順序與構(gòu)造順序相反

Copy and Move Operations

  • 合成的copy-control members中,each base class is implicitly constructed, assigned, or destroyed, using the corresponding member from that base class
  • 自定義的copy/move constructors and assignment operators must copy, move, or assign the whole object

derived-base conversion

  • a pointer or a reference to a derived class can be converted automatically to a pointer or a reference to an accessible base class
  • 栗子
// operations that take references to base classes of type Panda
void print(const Bear&);
void highlight(const Endangered&);
ostream& operator<<(ostream&, const ZooAnimal&);
Panda ying_yang("ying_yang");
print(ying_yang); // passes Panda to a reference to Bear
highlight(ying_yang); // passes Panda to a reference to Endangered
cout << ying_yang << endl; // passes Panda to a reference to ZooAnimal
  • converting to each base class is equally good
    void print(const Bear&);
    void print(const Endangered&);
    Panda ying_yang("ying_yang");
    print(ying_yang); // error: ambiguous
    

基類指針可見范圍

  • If we use a ZooAnimal pointer, only the operations defined in that class 和子類虛函數(shù) are usable(the Bear-specific, Panda-specific, and Endangered portions of the Panda interface are invisible)
  • 栗子(各類有的虛函數(shù)見下圖)
Endangered *pe = new Panda("ying_yang");
pe->print(); // ok: Panda::print()
pe->toes(); // error: not part of the Endangered interface
pe->cuddle(); // error: not part of the Endangered interface
pe->highlight(); // ok: Panda::highlight()
delete pe; // ok: Panda::~Panda()

Class Scope under Multiple Inheritance

  • the scope of a derived class is nested within the scope of its direct and indirect base classes
  • name lookup happens simultaneously among all the direct base classes
  • 若在多個基類中找到了該name罗标,則use of that name is ambiguous庸队,需要specify which version we want to use
  • 栗子:若ZooAnimal 和 Endangered 都定義了一個名為 max_weight 的成員,且 Panda 未定義該成員闯割,則下面的語句是錯誤的彻消,應該用ZooAnimal::max_weight 或 Endangered::max_weight:
double d = ying_yang.max_weight();

PS:name lookup happens before type checking

Virtual Inheritance


使用場景

  • 一個類不能在derivation list中include the same base class more than once,但一個類可以inherit from the same base class more than once(比如宙拉,iostream的兩個基類istream宾尚、ostream都繼承自basic_ios,iostream繼承了兩次basic_ios)
  • 而a derived object contains a separate subpart corresponding to each class in its derivation chain谢澈,所以iostream會有兩塊basic_ios
  • 但an iostream object wants to use the same buffer for both reading and writing煌贴;If an iostream object has two copies of its basic_ios class, this sharing isn’t possible
  • 所以使用virtual inheritance,the derived object contains only one, shared subobject for that virtual base class
  • 栗子

virtual繼承關系的定義

  • Panda has only one ZooAnimal base subpart
// the order of the keywords public and virtual is not significant
class Raccoon : public virtual ZooAnimal { /* ... */ };
class Bear : virtual public ZooAnimal { /* ... */ };

class Panda : public Bear, public Raccoon, public Endangered {
};

derived_base conversion仍然可用

void dance(const Bear&);
void rummage(const Raccoon&);
ostream& operator<<(ostream&, const ZooAnimal&);
Panda ying_yang;
dance(ying_yang); // ok: passes Panda object as a Bear
rummage(ying_yang); // ok: passes Panda object as a Raccoon
cout << ying_yang; // ok: passes Panda object as a ZooAnimal

Class Scope under Virtual Inheritance

  • 若虛基類中的某成員 is overridden along 0/1 個 derivation path锥忿,那么該成員 can be accessed directly 而不產(chǎn)生 ambiguity
  • 若虛基類中的某成員 is overridden along >=2 個 derivation path牛郑,那么在子類中直接調(diào)用該成員會造成 ambiguity
  • 栗子:B有成員x,D1敬鬓、D2虛繼承自B淹朋,D繼承自D1和D2灶似,當我們通過D直接使用x時
    1. 若D1和D2都沒有定義x,則沒有歧義瑞你,x會被解析為B-part的成員;
    2. 若D1和D2其中的一個(不妨設為D1)定義了x希痴,則沒有歧義者甲,x會被解析為D1-part的成員;
    3. 若D1和D2都定義了x砌创,則有歧義

constructor

  • the virtual base is initialized by the most derived constructor(比如when we create a Panda object, the Panda constructor alone controls how the ZooAnimal base class is initialized)
Panda::Panda(std::string name, bool onExhibit)
: ZooAnimal(name, onExhibit, "Panda"),
Bear(name, onExhibit),
Raccoon(name, onExhibit),
Endangered(Endangered::critical),
sleeping flag(false) { }
  • How a Virtually Inherited Object Is Constructed:先初始化虛基類-part虏缸,再按照derivation list的順序初始化直接基類-part,最后初始化子類-part嫩实;以Panda的構(gòu)造為例:
    1. the (virtual base class) ZooAnimal part is constructed first, using the initializers specified in the Panda constructor initializer list (若沒有則調(diào)用ZooAnimal的默認構(gòu)造函數(shù))
    2. the Bear part is constructed
    3. the Raccoon part is constructed
    4. the Endangered part is constructed
    5. the Panda part is constructed
  • Virtual base classes are always constructed prior to nonvirtual base classes regardless of where they appear in the inheritance hierarchy
  • 若有多個虛基類刽辙,按照derivation list中的順序構(gòu)造,比如:
class Character { /* ... */ };
class BookCharacter : public Character { /* ... */ };
class ToyAnimal { /* ... */ };
class TeddyBear : public BookCharacter,
public Bear, public virtual ToyAnimal { /* ... */ };

// TeddyBear 的構(gòu)造順序
ZooAnimal(); // Bear's virtual base class
ToyAnimal(); // direct virtual base class
Character(); // indirect base class of first nonvirtual base class
BookCharacter(); // first direct nonvirtual base class
Bear(); // second direct nonvirtual base class
TeddyBear(); // most derived class

PS:synthesized copy and move constructors甲献、synthesized assignment operators都遵從這種順序

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宰缤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子晃洒,更是在濱河造成了極大的恐慌慨灭,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件球及,死亡現(xiàn)場離奇詭異氧骤,居然都是意外死亡,警方通過查閱死者的電腦和手機吃引,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門筹陵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人镊尺,你說我怎么就攤上這事朦佩。” “怎么了鹅心?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵吕粗,是天一觀的道長。 經(jīng)常有香客問我旭愧,道長颅筋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任输枯,我火速辦了婚禮议泵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘桃熄。我一直安慰自己先口,他們只是感情好型奥,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著碉京,像睡著了一般厢汹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谐宙,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天烫葬,我揣著相機與錄音,去河邊找鬼凡蜻。 笑死搭综,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的划栓。 我是一名探鬼主播兑巾,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼忠荞!你這毒婦竟也來了蒋歌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤委煤,失蹤者是張志新(化名)和其女友劉穎奋姿,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體素标,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡称诗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了头遭。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寓免。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖计维,靈堂內(nèi)的尸體忽然破棺而出袜香,到底是詐尸還是另有隱情,我是刑警寧澤鲫惶,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布蜈首,位于F島的核電站,受9級特大地震影響欠母,放射性物質(zhì)發(fā)生泄漏欢策。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一赏淌、第九天 我趴在偏房一處隱蔽的房頂上張望踩寇。 院中可真熱鬧,春花似錦六水、人聲如沸俺孙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽睛榄。三九已至荣茫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間场靴,已是汗流浹背计露。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留憎乙,地道東北人。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓叉趣,卻偏偏與公主長得像泞边,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子疗杉,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348