2015-06-27 14 views
2

執行後Goomba::liveGoombas等於一些負值。我放棄了它,但不明白爲什麼它會多次構造函數啓動析構函數。爲什麼在這裏工作不正確?爲什麼靜態數據成員在發送到函數時更新不正確?

// Here is a simple Goomba class. It just keeps track of how many Goombas are alive. 

class Goomba 
{ 
public: 
    static int liveGoombas; 

    Goomba() { liveGoombas++; } 
    ~Goomba() { liveGoombas--; } 
}; 

int Goomba::liveGoombas = 0; 

// And a Goomba legion class. Please don't change this class. 
class GoombaLegion 
{ 
public: 
    void add(Goomba goomba) 
    { 
    goombas.push_back(goomba); //it seems that something wrong in this function 
    } 

private: 
    std::vector<Goomba> goombas; 
}; 

void goombas() 
{ 
    { 
    GoombaLegion legion; 
    } 

    // The legion went out of scope and was destroyed. But how many Goombas are alive? 
    std::cout << "There are " << Goomba::liveGoombas << " live goombas" << std::endl; 
} 



int main() 
{ 
    goombas(); 

} 
+3

當我編譯並運行它,我看到有0直播goombas。我有點困惑,因爲你寫的東西出了問題,但你永遠不叫GoombaLegion ::增加。 –

回答

4

如果不指定自己的實現,編譯器還會創建其他構造函數。這些是複製構造函數,對於C++ 11及更新版本,則是移動構造函數。

當您看到否定計數時,可以通過已使用的編譯器生成的構造函數之一來解釋。因此,實例的數量增加了,但liveGoombas沒有。

爲了獲得準確的數量,您應該將Goomba更改爲如下所示。

如果您在使用帶有移動語義編譯器啓用(的C++ 0x/C++ 11和更新版本):

class Goomba 
{ 
public: 
    static int liveGoombas; 

    Goomba() { liveGoombas++; } 
    Goomba(const Goomba&) { liveGoombas++; } 
    Goomba(Goomba&&) { liveGoombas++; } 

    // Need to explicitly state we want default or it will be deleted 
    // due to the above move constructor having been defined. 
    Goomba & operator = (const Goomba&) = default; 

    // Not really essential but including for completeness 
    Goomba & operator = (const Goomba&&) = default; 

    ~Goomba() { liveGoombas--; } 
}; 

否則:

class Goomba 
{ 
public: 
    static int liveGoombas; 

    Goomba() { liveGoombas++; } 
    Goomba(const Goomba&) { liveGoombas++; } 

    ~Goomba() { liveGoombas--; } 
}; 

默認賦值運算符是罰款因爲它們不會創建新的實例,而只是修改現有的實例。因爲它們的實例數量不應該被它們改變。

+0

你是否知道你正在刪除賦值運算符? – jimifiki

+0

@jimifiki我現在在:)感謝您的評論。我已經更新了我的答案。 – PeterSW

3

當通過值Goomba傳遞給您正在默默複製的函數時, 此外,您需要使用push_back操作以及向量偶爾會執行的可能重新分配來複制goombas。

爲了看到在代碼中你隱式地使用這些函數的默認構造的複製和賦值操作符,將它們定義爲私有的而沒有實現,編譯器會給你提供你需要的行的錯誤那些運營商。

也許你想給沿着構造函數的行的複製構造函數和賦值的實現。

C++ 11

如果定義了移動構造函數:

Goomba(const Goomba&&) { liveGoombas++; } 

你最好讓編譯器生成這兩個給你:

Goomba& operator=(const Goomba&&) = default; 
Goomba& operator=(const Goomba&) = default; 

否則他們是自動刪除。

如果不定義移動構造函數分配並沒有被刪除,但它仍然是一個好主意,明確規定了默認分配是對你有好處:

Goomba& operator=(const Goomba&) = default; 
+1

https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming) – xcvr

+0

謝謝@xcvr,用C++ 11有更多的,因爲C++ 11棄用的自動生成複製操作的類聲明 複製操作或析構函數。 – jimifiki

+2

當心 - 如果實現賦值運算符不增加的goomba算那裏。它不創建新的實例。 – PeterSW

0

沒有其他的答案是完全準確。

首先,請不要將移動構造函數聲明爲const參數,這是沒有意義的,也不是慣用的,因爲您至多可以獲得與副本相同的性能;而且,移動意味着修改「從...移開」的對象。

其次,有Goomba(Goomba&&) { liveGoombas++; }休息,如果你做到以下幾點:

Goomba g; 
Goomba b(std::move(g)); 
std::cout << Goomba::liveGoombas << std::endl; // prints 2, is that really correct? 

這應該單線程程序(live version here)做的伎倆:

class Goomba 
{ 
    bool alive; 
public: 
    static int liveGoombas; 

    Goomba() 
    : alive(true) 
    { liveGoombas++; } 

    Goomba(const Goomba& rhs) 
    : alive(rhs.alive) 
    { 
    if (alive) 
     liveGoombas++; 
    } 

    // Move constructor 
    Goomba(Goomba&& rhs) 
    : alive(std::move(rhs.alive)) 
    { 
    if (alive) 
     liveGoombas++; 
    rhs.kill(); // modifies moved from object! 
    } 

    Goomba& operator =(const Goomba& rhs) 
    { 
    kill(); 
    alive = rhs.alive; 
    if (alive) 
     liveGoombas++; 
    return *this; 
    } 

    // Move assignment 
    Goomba& operator =(Goomba&& rhs) 
    { 
    kill(); 
    alive = std::move(rhs.alive); 
    if (alive) 
     liveGoombas++; 
    rhs.kill(); // modifies moved from object! 
    return *this; 
    } 

    ~Goomba() { 
    kill(); 
    } 

private: 
    void kill() { 
    if (alive) { 
     liveGoombas--; 
     alive = false; 

     // insert other death/dying code here 
    } 
    } 
}; 

注:這是非常常見的要考慮的感動從物體被「破壞」,儘管事實並非如此。

相關問題