完整控制C++編程中結構函數應用的超等進修教程。本站提示廣大學習愛好者:(完整控制C++編程中結構函數應用的超等進修教程)文章只能為提供參考,不一定能成為您想要的結果。以下是完整控制C++編程中結構函數應用的超等進修教程正文
結構函數是一種可初始化其類的實例的成員函數。結構函數具有與類雷同的稱號,沒有前往值。結構函數可以具有隨意率性數目的參數,類可以具有隨意率性數目的重載結構函數。結構函數可以具有任何可拜訪性(公共、受掩護或公有)。假如不決義任何結構函數,則編譯器會生成不采取任何參數的默許結構函數;可以經由過程將默許結構函數聲明為已刪除來重寫此行動。
結構函數次序
結構函數按此次序履行任務:
按聲明次序挪用基類和成員結構函數。
假如類派生自虛擬基類,則會將對象的虛擬基指針初始化。
假如類具有或繼續了虛函數,則會將對象的虛函數指針初始化。虛函數指針指向類中的虛函數表,確保虛函數准確地挪用綁定代碼。
它履行本身函數體中的一切代碼。
上面的示例顯示,在派生類的結構函數中,基類和成員結構函數的挪用次序。起首,挪用基結構函數,然後依照基類成員在類聲明中湧現的次序對這些成員停止初始化,然後,挪用派生結構函數。
#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.
結構函數中的虛函數
我們建議你謹嚴挪用結構函數中的虛函數。基類結構函數一直在派生類結構函數之前挪用,是以基結構函數中挪用的函數是基類版本,而非派生類版本。鄙人面的示例中,結構 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_it Derived 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++ 界說兩種特別的結構函數(默許結構函數和復制結構函數),以下表所述。
默許結構函數和復制結構函數
默許結構函數可在沒有參數的情形下挪用。然則,假如一切參數都有默許值,則可以用參數列表聲明默許結構函數。異樣,復制結構函數必需接收對雷同類類型的援用的單一參數。可以供給多個參數,條件是一切後續參數都有默許值。
假如未供給任何結構函數,則編譯器將測驗考試生成默許結構函數。假如未供給復制結構函數,則編譯器將測驗考試生成一個。這些編譯器生成的結構函數被視為公共成員函數。假如應用屬於對象但不屬於援用的第一個參數指定復制結構函數,則將生成毛病。
編譯器生成的默許結構函數將設置對象(如上文所述,初始化 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。