1. 程式人生 > >C++基礎教程面向物件(學習筆記(30))

C++基礎教程面向物件(學習筆記(30))

複製初始化

考慮以下程式碼行:

int x = 5;

此語句使用拷貝初始化將新建立的整數變數x初始化為值5。

但是,類更復雜一些,因為它們使用建構函式進行初始化。本課將介紹與類的拷貝初始化相關的主題。

拷貝類的初始化

鑑於我們的Fraction類:

#include <cassert>
#include <iostream>
class Fraction
{
private:
    int m_numerator;
    int m_denominator;
 
public:
    // 預設建構函式
    Fraction(int numerator=0, int denominator=1) :
        m_numerator(numerator), m_denominator(denominator)
    {
        assert(denominator != 0);
    }
 
    friend std::ostream& operator<<(std::ostream& out, const Fraction &f1);
};
 
std::ostream& operator<<(std::ostream& out, const Fraction &f1)
{
	out << f1.m_numerator << "/" << f1.m_denominator;
	return out;
}

考慮以下:

int main()
{
    Fraction six = Fraction(6); 
    std::cout << six;
    return 0;
}

如果你要編譯並執行它,你會發現它產生了預期的輸出: 6/1 這種形式的複製初始化的評估方式與以下相同:

Fraction six(Fraction(6));

正如您在上一課中所瞭解到的,這可能會呼叫Fraction(int,int)和Fraction拷貝建構函式(由於效能原因可能會被省略)。但是,由於不能保證刪除,最好避免類的拷貝初始化,而是使用直接或統一初始化。

規則:避免使用複製初始化,而是使用統一初始化。

其他地方使用拷貝初始化

還有一些其他地方使用了拷貝初始化,但其中兩個值得明確提及。按值傳遞或返回類時,該程序使用複製初始化。

考慮:

#include <cassert>
#include <iostream>
 
class Fraction
{
private:
	int m_numerator;
	int m_denominator;
 
public:
    //預設建構函式
    Fraction(int numerator=0, int denominator=1) :
        m_numerator(numerator), m_denominator(denominator)
    {
        assert(denominator != 0);
    }
 
        // 拷貝建構函式
	Fraction(const Fraction &copy) :
		m_numerator(copy.m_numerator), m_denominator(copy.m_denominator)
	{
		// 這裡不需要檢查0的分母,因為副本必須已經是有效的分數
		std::cout << "Copy constructor called\n"; // just to prove it works
	}
 
	friend std::ostream& operator<<(std::ostream& out, const Fraction &f1);
        int getNumerator() { return m_numerator; }
        void setNumerator(int numerator) { m_numerator = numerator; }
};
 
std::ostream& operator<<(std::ostream& out, const Fraction &f1)
{
	out << f1.m_numerator << "/" << f1.m_denominator;
	return out;
}
 
Fraction makeNegative(Fraction f) // 理想情況下我們應該通過const引用來做到這一點
{
    f.setNumerator(-f.getNumerator());
    return f;
}
 
int main()
{
    Fraction fiveThirds(5, 3);
    std::cout << makeNegative(fiveThirds);
 
    return 0;
}

在上面的程式中,函式makeNegative按值獲取分數,並返回按值分數。當我們執行這個程式時,我們得到: Copy constructor called Copy constructor called -5/3 當fiveThirds作為引數傳遞給makeNegative()引數f時,會發生第一個拷貝建構函式呼叫。當makeNegative()的返回值傳遞迴main()時,會發生第二次呼叫。

在上面的例子中,不能省略通過值傳遞的引數和返回值。但是,在其他情況下,如果引數或返回值滿足特定條件,編譯器可能會選擇忽略拷貝建構函式。例如:

class Something
{
};
 
Something foo() 
{
  Something s;
  return s;
}
 
int main()
{
  Something s = foo();
}

在這種情況下,編譯器可能會忽略拷貝建構函式,即使變數s是按值返回的。