亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C++ > 正文

完全掌握C++編程中構造函數使用的超級學習教程

2020-05-23 14:09:39
字體:
來源:轉載
供稿:網友
這篇文章主要介紹了C++中的構造函數,包括C++11標準中的新特性的介紹,十分推薦!需要的朋友可以參考下
 

構造函數是一種可初始化其類的實例的成員函數。構造函數具有與類相同的名稱,沒有返回值。構造函數可以具有任意數量的參數,類可以具有任意數量的重載構造函數。構造函數可以具有任何可訪問性(公共、受保護或私有)。如果未定義任何構造函數,則編譯器會生成不采用任何參數的默認構造函數;可以通過將默認構造函數聲明為已刪除來重寫此行為。
構造函數順序
構造函數按此順序執行工作:
按聲明順序調用基類和成員構造函數。
如果類派生自虛擬基類,則會將對象的虛擬基指針初始化。
如果類具有或繼承了虛函數,則會將對象的虛函數指針初始化。虛函數指針指向類中的虛函數表,確保虛函數正確地調用綁定代碼。
它執行自己函數體中的所有代碼。
下面的示例顯示,在派生類的構造函數中,基類和成員構造函數的調用順序。首先,調用基構造函數,然后按照基類成員在類聲明中出現的順序對這些成員進行初始化,然后,調用派生構造函數。

#include <iostream>using namespace std;class Contained1 {public:  Contained1() {    cout << "Contained1 constructor." << endl;  }};class Contained2 {public:  Contained2() {    cout << "Contained2 constructor." << endl;  }};class Contained3 {public:  Contained3() {    cout << "Contained3 constructor." << endl;  }};class BaseContainer {public:  BaseContainer() {    cout << "BaseContainer constructor." << endl;  }private:  Contained1 c1;  Contained2 c2;};class DerivedContainer : public BaseContainer {public:  DerivedContainer() : BaseContainer() {    cout << "DerivedContainer constructor." << endl;  }private:  Contained3 c3;};int main() {  DerivedContainer dc;  int x = 3;}

這是輸出:

Contained1 constructor.Contained2 constructor.BaseContainer constructor.Contained3 constructor.DerivedContainer constructor.

如果構造函數引發異常,析構的順序與構造的順序相反:
構造函數主體中的代碼將展開。
基類和成員對象將被銷毀,順序與聲明順序相反。
如果是非委托構造函數,所有完全構造的基類對象和成員均將被銷毀。但是,對象本身不是完全構造的,因此析構函數不會運行。
成員列表
使用成員初始值設定項列表從構造函數參數初始化類成員。此方法使用直接初始化,這比在構造函數體內使用賦值運算符更高效。

class Box {public:  Box(int width, int length, int height)     : m_width(width), m_length(length), m_height(height) // member init list  {}  int Volume() {return m_width * m_length * m_height; }private:  int m_width;  int m_length;  int m_height;};

創建 Box 對象:

Box b(42, 21, 12);cout << "The volume is " << b.Volume();

顯式構造函數
如果類具有帶一個參數的構造函數,或是如果除了一個參數之外的所有參數都具有默認值,則參數類型可以隱式轉換為類類型。例如,如果 Box 類具有一個類似于下面這樣的構造函數:

Box(int size): m_width(size), m_length(size), m_height(size){}

可以初始化 Box,如下所示:

Box b = 42;

或將一個 int 傳遞給采用 Box 的函數:

class ShippingOrder{public:  ShippingOrder(Box b, double postage) : m_box(b), m_postage(postage){}private:  Box m_box;  double m_postage;}//elsewhere...  ShippingOrder so(42, 10.8);


這類轉換可能在某些情況下很有用,但更常見的是,它們可能會導致代碼中發生細微但嚴重的錯誤。作為一般規則,應對構造函數使用 explicit 關鍵字(和用戶定義的運算符)以防止出現這種隱式類型轉換:

explicit Box(int size): m_width(size), m_length(size), m_height(size){}

構造函數是顯式函數時,此行會導致編譯器錯誤:ShippingOrder so(42, 10.8);。
默認構造函數
默認構造函數沒有參數;它們遵循略有不同的規則:
默認構造函數是一個特殊成員函數;如果沒有在類中聲明構造函數,則編譯器會提供默認構造函數:

class Box {  Box(int width, int length, int height)     : m_width(width), m_length(length), m_height(height){}};int main(){  Box box1{}; // call compiler-generated default ctor  Box box2;  // call compiler-generated default ctor}

當你調用默認構造函數并嘗試使用括號時,系統將發出警告:

class myclass{};int main(){myclass mc();   // warning C4930: prototyped function not called (was a variable definition intended?)}

這是“最棘手的解析”問題的示例。這種示例表達式既可以解釋為函數的聲明,也可以解釋為對默認構造函數的調用,而且 C++ 分析器更偏向于聲明,因此表達式會被視為函數聲明。 
如果聲明了任何非默認構造函數,編譯器不會提供默認構造函數:

class Box {  Box(int width, int length, int height)     : m_width(width), m_length(length), m_height(height){}};private:  int m_width;  int m_length;  int m_height;};int main(){  Box box1(1, 2, 3);  Box box2{ 2, 3, 4 };  Box box4;   // compiler error C2512: no appropriate default constructor available}

如果類沒有默認構造函數,將無法通過單獨使用方括號語法來構造該類的對象數組。例如,在前面提到的代碼塊中,框的數組無法進行如下聲明:

Box boxes[3];  // compiler error C2512: no appropriate default constructor available

但是,你可以使用初始值設定項列表將框的數組初始化:

Box boxes[3]{ { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };

復制和移動構造函數
復制構造函數是特殊成員函數,它采用對相同類型對象的引用作為輸入,并創建它的副本。移動也是特殊成員函數構造函數,它將現有對象的所有權移交給新變量,而不復制原始數據。

顯式默認構造函數和已刪除構造函數
你可以顯式設置默認復制構造函數、設置默認構造函數、移動構造函數、復制賦值運算符、移動賦值運算符和析構函數。你可以顯式刪除所有特殊成員函數。 
派生類中的構造函數
派生類構造函數始終調用基類構造函數,因此,在完成任何額外任務之前,它可以依賴于完全構造的基類。調用基類構造函數進行派生,例如,如果 ClassA 派生自 ClassB,ClassB 派生自 ClassC,那么首先調用 ClassC 構造函數,然后調用 ClassB 構造函數,最后調用 ClassA 構造函數。
如果基類沒有默認構造函數,則必須在派生類構造函數中提供基類構造函數參數:

class Box {public:  Box(int width, int length, int height){    m_width = width;    m_length = length;    m_height = height;  }private:  int m_width;  int m_length;  int m_height;};class StorageBox : public Box {public:  StorageBox(int width, int length, int height, const string label&) : Box(width, length, height){    m_label = label;  }private:  string m_label;};int main(){  const string aLabel = "aLabel";  StorageBox sb(1, 2, 3, aLabel);} 

具有多重繼承的類的構造函數
如果類從多個基類派生,那么將按照派生類聲明中列出的順序調用基類構造函數:

#include <iostream>using namespace std;class BaseClass1 {public:  BaseClass1() {    cout << "BaseClass1 constructor." << endl;  }};class BaseClass2 {public:  BaseClass2() {    cout << "BaseClass2 constructor." << endl;  }};class BaseClass3{public:  BaseClass3() {    cout << "BaseClass3 constructor." << endl;  }};class DerivedClass : public BaseClass1, public BaseClass2, public BaseClass3 {public:  DerivedClass() {    cout << "DerivedClass constructor." << endl;  }};int main() {  DerivedClass dc;}

你應看到以下輸出:

BaseClass1 constructor.BaseClass2 constructor.BaseClass3 constructor.DerivedClass constructor.

構造函數中的虛函數
我們建議你謹慎調用構造函數中的虛函數?;悩嬙旌瘮凳冀K在派生類構造函數之前調用,因此基構造函數中調用的函數是基類版本,而非派生類版本。在下面的示例中,構造 DerivedClass 會導致執行 BaseClass 的 print_it() 實現早于 DerivedClass 構造函數導致執行 DerivedClass 的 print_it() 實現:

#include <iostream>using namespace std;class BaseClass{public:  BaseClass(){    print_it();  }  virtual void print_it() {    cout << "BaseClass print_it" << endl;  }};class DerivedClass : public BaseClass {public:  DerivedClass() {    print_it();  }  virtual void print_it(){    cout << "Derived Class print_it" << endl;  }};int main() {  DerivedClass dc;}

這是輸出:

BaseClass print_itDerived Class print_it

構造函數和復合類
包含類類型成員的類稱為“復合類”。創建復合類的類類型成員時,調用類自己的構造函數之前,先調用構造函數。當包含的類沒有默認構造函數是,必須使用復合類構造函數中的初始化列表。在之前的 StorageBox 示例中,如果將 m_label 成員變量的類型更改為新的 Label 類,則必須調用基類構造函數,并且將 m_label 變量(位于 StorageBox 構造函數中)初始化:

class Label {public:  Label(const string& name, const string& address) { m_name = name; m_address = address; }  string m_name;  string m_address;};class StorageBox : public Box {public:  StorageBox(int width, int length, int height, Label label)     : Box(width, length, height), m_label(label){}private:  Label m_label;};int main(){// passing a named Label  Label label1{ "some_name", "some_address" };  StorageBox sb1(1, 2, 3, label1);  // passing a temporary label  StorageBox sb2(3, 4, 5, Label{ "another name", "another address" });  // passing a temporary label as an initializer list  StorageBox sb3(1, 2, 3, {"myname", "myaddress"});}

委托構造函數
委托構造函數調用同一類中的其他構造函數,完成部分初始化工作。在下面的示例中,派生類具有三個構造函數,第二個構造函數委托第一個,第三個構造函數委托第二個:

#include <iostream>using namespace std;class ConstructorDestructor {public:  ConstructorDestructor() {    cout << "ConstructorDestructor default constructor." << endl;  }  ConstructorDestructor(int int1) {    cout << "ConstructorDestructor constructor with 1 int." << endl;  }  ConstructorDestructor(int int1, int int2) : ConstructorDestructor(int1) {    cout << "ConstructorDestructor constructor with 2 ints." << endl;    throw exception();  }  ConstructorDestructor(int int1, int int2, int int3) : ConstructorDestructor(int1, int2) {    cout << "ConstructorDestructor constructor with 3 ints." << endl;  }  ~ConstructorDestructor() {    cout << "ConstructorDestructor destructor." << endl;  }};int main() {  ConstructorDestructor dc(1, 2, 3);}

這是輸出:

ConstructorDestructor constructor with 1 int.ConstructorDestructor constructor with 2 ints.ConstructorDestructor constructor with 3 ints.

所有構造函數完成后,完全初始化的構造函數將立即創建對象。 DerivedContainer(int int1) 成功,但是 DerivedContainer(int int1, int int2) 失敗,并調用析構函數。

class ConstructorDestructor {public:  ConstructorDestructor() {    cout << "ConstructorDestructor default constructor." << endl;  }  ConstructorDestructor(int int1) {    cout << "ConstructorDestructor constructor with 1 int." << endl;  }  ConstructorDestructor(int int1, int int2) : ConstructorDestructor(int1) {    cout << "ConstructorDestructor constructor with 2 ints." << endl;    throw exception();  }  ConstructorDestructor(int int1, int int2, int int3) : ConstructorDestructor(int1, int2) {    cout << "ConstructorDestructor constructor with 3 ints." << endl;  }  ~ConstructorDestructor() {    cout << "ConstructorDestructor destructor." << endl;  }};int main() {  try {    ConstructorDestructor cd{ 1, 2, 3 };  }  catch (const exception& ex){  }}

輸出:

ConstructorDestructor constructor with 1 int.ConstructorDestructor constructor with 2 ints.ConstructorDestructor destructor.

繼承構造函數 (C++11)
派生類可以使用 using 聲明從直接基類繼承構造函數,如下面的示例所示:

#include <iostream>using namespace std;class Base{public:    Base() { cout << "Base()" << endl; }  Base(const Base& other) { cout << "Base(Base&)" << endl; }  explicit Base(int i) : num(i) { cout << "Base(int)" << endl; }  explicit Base(char c) : letter(c) { cout << "Base(char)" << endl; }private:  int num;  char letter;};class Derived : Base{public:  // Inherit all constructors from Base  using Base::Base;private:  // Can't initialize newMember from Base constructors.  int newMember{ 0 };};int main(int argc, char argv[]){  cout << "Derived d1(5) calls: ";   Derived d1(5);  cout << "Derived d1('c') calls: ";  Derived d2('c');  cout << "Derived d3 = d2 calls: " ;  Derived d3 = d2;  cout << "Derived d4 calls: ";  Derived d4;   // Keep console open in debug mode:  cout << endl << "Press Enter to exit.";  char in[1];  cin.getline(in, 1);  return 0;}
輸出:
Derived d1(5) calls: Base(int)Derived d1('c') calls: Base(char)Derived d3 = d2 calls: Base(Base&)Derived d4 calls: Base()Press Enter to exit.
 

using 語句可將來自基類的所有構造函數引入范圍(除了簽名與派生類中的構造函數相同的構造函數)。一般而言,當派生類未聲明新數據成員或構造函數時,最好使用繼承構造函數。
如果類型指定基類,則類模板可以從類型參數繼承所有構造函數:

template< typename T >class Derived : T {  using T::T;  // declare the constructors from T  // ...};

如果基類的構造函數具有相同簽名,則派生類無法從多個基類繼承。
聲明構造函數的規則
構造函數與它的類的名稱相同??梢月暶魅我鈹盗康臉嬙旌瘮?,這取決于重載函數的規則。

argument-declaration-list 可能為空。
C++ 定義兩種特殊的構造函數(默認構造函數和復制構造函數),如下表所述。

完全掌握C++編程中構造函數使用的超級學習教程

默認構造函數和復制構造函數

默認構造函數可在沒有參數的情況下調用。但是,如果所有參數都有默認值,則可以用參數列表聲明默認構造函數。同樣,復制構造函數必須接受對相同類類型的引用的單一參數??梢蕴峁┒鄠€參數,前提是所有后續參數都有默認值。
如果未提供任何構造函數,則編譯器將嘗試生成默認構造函數。如果未提供復制構造函數,則編譯器將嘗試生成一個。這些編譯器生成的構造函數被視為公共成員函數。如果使用屬于對象但不屬于引用的第一個參數指定復制構造函數,則將生成錯誤。
編譯器生成的默認構造函數將設置對象(如上文所述,初始化 vftables 和 vbtables),并調用基類和成員的默認構造函數,但是它不執行任何其他操作。僅當基類和成員構造函數存在、可訪問并且無歧義時才會調用它們。
編譯器生成的復制構造函數將設置新的對象,并對要復制的對象的內容按成員復制。如果基類或成員構造函數存在,則將調用它們;否則將執行按位復制。
如果類 type 的所有基類和成員類均具有接受 const 參數的復制構造函數,則編譯器生成的復制構造函數將接受 const type& 類型的單個參數。否則,編譯器生成的復制構造函數將接受 type& 類型的單個參數。
您可以使用構造函數初始化 const 或 volatile 對象,但是,構造函數本身不能聲明為 const 或 volatile。構造函數的唯一合法存儲類是 inline;將任何其他存儲類修飾符(包括 __declspec 關鍵字)與構造函數一起使用將導致編譯器錯誤。
stdcall 調用約定用于使用 __stdcall 關鍵字聲明的靜態成員函數和全局函數,且不使用變量參數列表。對非靜態成員函數(如構造函數)使用 __stdcall 關鍵字時,編譯器將使用 thiscall 調用約定。
基類的構造函數不由派生類繼承。創建派生類類型的對象時,該對象將從基類組件開始進行構造;然后移到派生類組件。由于整個對象有一部分已初始化,因此編譯器使用每個基類的構造函數(虛擬派生的情況除外,如初始化基類中所述)。
顯式調用構造函數
可以在程序中顯式調用構造函數來創建給定類型的對象。例如,若要創建描述某行末尾的兩個 Point 對象,請編寫以下代碼:

DrawLine( Point( 13, 22 ), Point( 87, 91 ) );

創建類型 Point 的兩個對象,將其傳遞給函數 DrawLine,并在表達式(函數調用)的末尾將其銷毀。
在其中顯式調用構造函數的另一個上下文正在進行初始化:

Point pt = Point( 7, 11 );

使用接受類型為 Point 的兩個參數的構造函數來創建和初始化類型為 int 的對象。
通過顯式調用構造函數創建的對象(如上面的兩個示例)未進行命名,并且該對象具有在其中創建它們的表達式的生存期。 臨時對象中更詳細地討論了這一點。
通常,從構造函數的內部調用所有成員函數是安全的,因為該對象在用戶代碼的第一行執行之前已完全設置(已初始化虛擬表等)。但是,在構造或析構期間,成員函數調用抽象基類的虛擬成員函數可能是不安全的。
構造函數可以調用虛函數。調用虛函數時,調用的函數將是為構造函數自己的類定義的函數(或從其基類繼承)。以下示例演示從構造函數的內部調用虛函數時發生的情況:

// specl_calling_virtual_functions.cpp// compile with: /EHsc#include <iostream>using namespace std;class Base{public:  Base();       // Default constructor.  virtual void f();  // Virtual member function.};Base::Base(){  cout << "Constructing Base sub-object/n";  f();        // Call virtual member function}            // from inside constructor.void Base::f(){  cout << "Called Base::f()/n";}class Derived : public Base{public:  Derived();     // Default constructor.  void f();      // Implementation of virtual};           // function f for this class.Derived::Derived(){  cout << "Constructing Derived object/n";}void Derived::f(){  cout << "Called Derived::f()/n";}int main(){  Derived d;}

在運行前面的程序時,聲明 Derived d 將產生以下事件序列:
調用類 Derived (Derived::Derived) 的構造函數。
在輸入 Derived 類的構造函數的主體之前,調用類 Base (Base::Base) 的構造函數。
Base::Base 調用函數 f,該函數是一個虛函數。通常,將調用 Derived::f,因為對象 d 屬于類型 Derived。由于 Base::Base 函數是構造函數,因此該對象不屬于 Derived 類型,并且將調用 Base::f。



發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品入口福利| 北条麻妃一区二区三区中文字幕| 亚洲欧美在线磁力| 亚洲图片在线综合| 国产欧美在线看| 狠狠躁夜夜躁久久躁别揉| 欧美最猛性xxxx| 日韩av男人的天堂| 国产精品视频自拍| 国产精品久久久久久久久久新婚| 亚洲一区www| 国产精品国产三级国产aⅴ浪潮| 91视频-88av| 国产精品综合不卡av| 久久久免费在线观看| 国产伦精品一区二区三区精品视频| 亚洲二区中文字幕| 欧美激情手机在线视频| 国产精品色视频| 日韩最新免费不卡| 日本免费一区二区三区视频观看| 91国产高清在线| 黄网站色欧美视频| 亚洲精品综合久久中文字幕| 亚洲精品视频在线播放| 国产激情综合五月久久| 欧美极品欧美精品欧美视频| 亚洲人成电影在线| 国产精自产拍久久久久久| 91久久夜色精品国产网站| 美女精品久久久| 中文字幕av一区中文字幕天堂| 日本高清久久天堂| 亚洲mm色国产网站| 亚洲加勒比久久88色综合| 国产精品视频一区国模私拍| 欧美成人在线网站| 精品久久久久久久久久ntr影视| 久久精品一区中文字幕| 欧美一区二区三区免费观看| 日本欧美在线视频| 中文字幕久久久av一区| 精品国内产的精品视频在线观看| 久久久久久久亚洲精品| 久久琪琪电影院| 欧美第一淫aaasss性| 成人黄色免费网站在线观看| 欧美日韩国产精品一区| 亚洲欧美制服丝袜| 黑人极品videos精品欧美裸| 美女国内精品自产拍在线播放| 91精品国产777在线观看| 国产91色在线| 青草青草久热精品视频在线观看| 欧美综合在线第二页| 国产精品久久久久久久久久久久| 91超碰caoporn97人人| 国外日韩电影在线观看| 97香蕉久久超级碰碰高清版| 亚洲成人性视频| 午夜精品久久久久久久男人的天堂| 日韩av中文字幕在线| 国产视频久久久久| 欧美孕妇毛茸茸xxxx| 日韩精品免费电影| 97在线观看视频| 日韩av在线免费| 欧美最猛性xxxxx(亚洲精品)| 国产成人精品亚洲精品| 欧美精品一区二区三区国产精品| 亚洲国产精品一区二区久| 亚州欧美日韩中文视频| 午夜精品在线视频| 亚洲男人天堂手机在线| 国产精品成人一区二区三区吃奶| 成人精品久久一区二区三区| 国产婷婷97碰碰久久人人蜜臀| 精品国产91久久久久久老师| 国产在线视频2019最新视频| 欧美一区二区三区免费观看| 91超碰中文字幕久久精品| 一二美女精品欧洲| 欧美在线观看网站| 欧美成人午夜激情| 亚洲最大成人网色| 久久久午夜视频| 亚洲精品动漫久久久久| 久久综合伊人77777蜜臀| 亚洲国产精品电影在线观看| 成人精品在线观看| 日韩欧美中文免费| 国产精品爱啪在线线免费观看| 91wwwcom在线观看| 国产综合在线看| 久久国产精品久久久久久久久久| 亚洲国产中文字幕久久网| 日本一区二三区好的精华液| 日韩av综合网| 中文字幕少妇一区二区三区| 国产精品午夜视频| 国产午夜精品麻豆| 日韩av日韩在线观看| 国内精品久久久久伊人av| 91精品国产自产91精品| 国产在线观看精品| 欧美野外猛男的大粗鳮| 欧美精品免费在线观看| 国产91精品久久久久久| 久久99久久99精品中文字幕| 日韩视频亚洲视频| 成人免费视频xnxx.com| 亚洲人成电影在线播放| 日韩欧美中文免费| 日韩中文字幕精品视频| 欧美亚洲在线观看| 欧美一区二区三区艳史| 国语自产精品视频在线看一大j8| 91精品久久久久久久久久入口| 成人av资源在线播放| 日韩av电影手机在线| 欧美成人国产va精品日本一级| 亚洲国产精品人久久电影| 国产97色在线| 久久91精品国产91久久跳| 欧美成在线观看| 在线视频免费一区二区| 欧美亚洲国产另类| 亚洲欧美国产精品久久久久久久| 97精品久久久中文字幕免费| 久久久久免费视频| 国产精品黄色影片导航在线观看| 国产精品精品久久久| 九九久久精品一区| 亚洲第一中文字幕在线观看| 日日骚久久av| 日韩av在线影视| 亚洲免费高清视频| 成人福利在线观看| 热久久这里只有精品| 久久久国产精品x99av| 91干在线观看| 亚洲欧美精品suv| 日韩高清电影好看的电视剧电影| 久久不射热爱视频精品| 亚洲视频第一页| 亚洲午夜精品久久久久久久久久久久| 欧美性视频精品| 久久精品亚洲94久久精品| 日韩免费看的电影电视剧大全| 欧美高跟鞋交xxxxhd| 国模叶桐国产精品一区| 国产国语videosex另类| 欧美激情一区二区三区在线视频观看| 亚洲精品v天堂中文字幕| 亚洲欧美日韩高清| 亚洲精品欧美日韩专区| 日韩成人高清在线| 国产精品亚发布| 国产精品久久久久久久av电影| 国产精品草莓在线免费观看| 国产精品96久久久久久| 色777狠狠综合秋免鲁丝| 午夜精品福利电影| 国产精品美女网站|