簡體   English   中英

C++ 封裝和構造函數中的引用

[英]C++ Encapsulation and references in constructor

當有人傳遞參數時,我無法將封裝的概念(其他人不應直接更改對象數據)和良好的性能建議放在一起。

對於此代碼

// Code only for illustration, I'm not looking for syntax comments (thanks)
class Person{
private:
    std::string name;
    std::string Id;

public:
    Person(std::string _name, std::vector<double> vec)...

我知道最推薦使用的是參數的const引用(甚至編譯器也警告過)。 但這讓對象依賴於調用它們的代碼嗎?

在上面的代碼中,如果我通過引用傳遞vector ,並且對象需要創建它的副本或修改,不要通過相同的值傳遞?。 當其他人修改引用的向量時,對象會發生什么?

希望這是可讀的

如果你有課

class Person{
    std::string name;
public:
    Person(std::string _name){
        //Body of constructor
    }
};

那么在構造函數的主體中有兩個字符串可以訪問:被構造的Person實例的成員name ,以及_name的參數_name 通常,構造函數會做類似*

    Person(std::string _name){
        name = _name;
    }

上面寫着“將_name值復制到name ” - 並且具有所需的行為,即name是一個獨立於其他任何東西的對象。

這樣做的問題是,如果我們在其他地方想要構造Person的實例並且已經有一個字符串 - 例如我們編寫了代碼

std::string namePassed = "Alice";
Person person(namePassed);

然后編譯器必須生成代碼以按值傳遞namePassed - 這意味着編譯器會將namePassed復制到一個新位置,該位置將由構造_name體內的_name引用。 這意味着構造過程中發生的整個事件序列如下:

  1. namePassed被復制到參數_name

  2. 參數_name被復制到成員name

  3. 參數_name被破壞。

我們真正想要發生的是, namedPassed會被簡單地復制**到成員name ——我們不想無緣無故地制作臨時副本!

解決這個問題的方法是通過 const 引用傳遞_name作為

    Person(std::string const& _name){
        name = _name;
    }

因為當我們寫

std::string namePassed = "Alice";
Person person(namePassed);

編譯器說_name是對namePassed的引用(本質上是一個指針),然后從該引用復制到name - 這意味着成員name仍然以獨立副本結束,但我們避免了額外的復制操作。 如果我們稍后分配給namePassed ,那也沒關系,因為成員name是一個副本,與namePassed不共享任何狀態,並且參數_name將離開作用域***。 const&限定只適用於臨時參數,不適用於成員。

如果要存儲成員引用,則必須將該成員聲明為常量引用,而不僅僅是參數。 那看起來像這樣:

class Person{
    std::string const& name;
public:
    Person(std;:string const& _name):name(_name){}
};

在一些特殊情況之外,這是一個壞主意,因為現在只要 Person 的實例存在,對字符串的引用就必須活着,並且對引用字符串的更改將反映在類中 - 但請注意,正在存儲的成員被標記為const&而不僅僅是參數。


*這里的代碼不是慣用的,因為我想強調復制操作。 更常見的是只寫

    Person(std::string const& _name):name(_name){}

它使用初始化列表。 前面的代碼說“默認初始化name (即初始化一個空字符串),然后為其分配一個值”,而此代碼直接調用帶有給定參數的name的復制構造函數。

**移動(右值)語義有點復雜,但這有點超出了這個答案的范圍。 值得注意的是,有一個相當常見的構造,其中一個傳遞值然后移動:

    Person(std::string _name):name(std::move(_name)){}

這表達了我們可以傳入一個值_name的想法,該值保證是字符串的獨立副本。 然后,知道它是一個副本,我們可以移動到位,這基本上意味着我們竊取了_name使用的內存並將其交給了name ,這比復制整個字符串便宜得多,而且比編寫單獨的構造函數更不煩人右值和左值。

*** 可能會出現錯誤,但是,如果namePassed的值在構造函數的執行期間設法更改,但在進行復制之前,則可能會從錯誤的來源復制name 這主要是編寫多線程代碼時出現的問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM