C++ 對象初始化和賦值


1.區分初始化和賦值
  在C++中,當一個新對象被創建時,會有初始化操作;而賦值是修改一個已經存在的對象的值。
  初始化操作由構造函數完成,而賦值操作由operator=操作符函數完成。如果程序員沒有提供構造函數和operator=操作符函數,那么編譯器會提供缺省版本,使用缺省構造函數或缺省復制構造函數初始化對象,使用缺省operator=操作符函數執行賦值操作。舉例如下:

View Code
/**
 * @file Main.cpp
 */
#include <iostream>
using std::cout;
using std::endl;

class CDummy
{
public:
    CDummy(void);
    CDummy(const CDummy& dummy);
    CDummy& operator=(const CDummy& dummy);
};

CDummy::CDummy()
{
    cout << "無參構造" << endl;
}

CDummy::CDummy(const CDummy& /*dummy*/)
{
    cout << "復制構造" << endl;
}

CDummy& CDummy::operator=(const CDummy& /*dummy*/)
{
    cout << "賦值操作" << endl;
    return *this;
}

int main(int argc, char* argv[])
{
    CDummy d1;      //初始化,無參構造函數被調用
    CDummy d2 = d1; //初始化,復制構造函數被調用
    CDummy d3;      //初始化,無參構造函數被調用
    d3 = d1;        //賦值,operator=操作符函數被調用
    return 0;
}

  另外要注意,在賦值時被賦值對象已經有值,因此可能需要對原值做適當處理,特別是資源的歸還和重新申請等等。在這方面,String類的operator=操作符函數的實現是典型例子。

2.關於成員初始化列表和構造函數內賦值操作
  先看示例程序:

View Code
class PhoneNumber
{
};

class AddressBookEntry
{
public:
    AddressBookEntry(const string& name, 
                     const string& address, 
                     const list<PhoneNumber>& phones);
private:
    string _name;
    string _address;
    list<PhoneNumber> _phones;
};

AddressBookEntry::AddressBookEntry(const string& name, 
                                   const string& address, 
                                   const list<PhoneNumber>& phones)
{
    _name = name;       //這些都是賦值,而非初始化
    _address = address;
    _phones = phones;
}

  C++規定,對象的成員變量的初始化動作發生在進入構造函數本體之前。在AddressBookEntry構造函數內,_name,_address,_phones都不是被初始化,而是被賦值。初始化的發生時間更早,發生於這些成員的default構造函數被自動調用之時(比進入AddressBookEntry構造函數本體的時間更早)。
  AddressBookEntry構造函數的一個較佳寫法是,使用成員初始化列表代替賦值動作:

View Code
AddressBookEntry::AddressBookEntry(const string& name,
                                   const string& address,
                                   const list<PhoneNumber>& phones)
                                   :_name(name),       //現在,這些都是初始化
                                    _address(address),
                                    _phones(phones)
{
}

  賦值的那個版本先調用default構造函數然后再調用operator=操作符函數;而成員初始化列表的那個版本只調用一次copy構造函數,是比較高效的,有時甚至高效得多。
  另外,對於內置類型對象,其初始化和賦值的成本相同。但是有些情況下即使成員變量屬於內置類型,也一定得使用成員初始化列表。是的,如果成員變量是 const 或 references,它們就一定需要初值,不能被賦值。

3.關於成員初始化次序
  C++有着十分固定的成員初始化次序。是的,次序總是相同:base classes 更早於其 derived classes被初始化,而 class 的成員變量總是以其聲明次序被初始化。
  另外,為避免“跨編譯單元之初始化次序”問題,請以 local static 對象替換 non-local static 對象。這是 Singleton 模式的一個常見實現手法。(ACE里面的單例大多是這么實現的,原來是有原因的:))


參考文獻:
Effective C++ 3rd Edition


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM