2014-10-16 116 views
0

請考慮下面的代碼:爲什麼發生切片?

Base b; 
if (something) 
    b = DerivedA(); 
else 
    b = DerivedB(); 

這是衆所周知,在這樣的情況下,「切片」時:在C++中,我們不能分配鹼的變量類型的派生類型的對象;該對象將被「切割」掉任何未在基類型中定義的東西。 (如果我們想要做這樣的事情,我們必須使用指針或引用)。

我想了解這個的實際原因。也就是說,Base變量在沒有切片的情況下不能容納Derived對象。

我的假設是,這樣做的原因是,Base對象和Derived對象可能不是相同的大小,因此,我們不能能夠存儲整個Derived對象在Base變量做出保證。 A Base可能佔用4個字節,而Derived爲7個字節。所以我們決定總是對派生對象進行切片以適應基本類型的大小。

我們能夠用指針做到這一點,因爲它們都佔用相同數量的內存。

這個假設是否正確?如果不是,切片的實際原因是什麼?

+1

你回答你自己的問題。 7字節不能進入4. – 2014-10-16 22:04:09

+0

@MattMcNabb好吧,那是我的假設,想要確定。想想也許這是更多的設計問題。順便說一句,爲什麼我們不能調整變量來適應指定的對象? – 2014-10-16 22:04:47

+1

這正是切片所做的。這只是不按照你想要的方向調整大小。 – 2014-10-16 22:06:06

回答

2

問題是複製和移動語義(複製構造函數,複製分配等)。您正在獲取元素的副本,但並非所有元素都將被複制。如果你有一個基地指針,就沒有問題了。

如果您有一個完全填充的DerivedA對象,並將其分配給本地堆棧基本類型,將使用複製分配,並且任何派生元素值將被丟棄。

考慮當你寫你的複製構造函數。除了當前班級的成員之外,你還有其他工作嗎?你如何知道當前課程的內容,以及需要做什麼?試圖這樣做會很糟糕。

class BaseType 
{ 
private: 
    int m_i; 

public: 
    explicit BaseType(BaseType const & other) // copy ctor 
    { 
     m_i = other.m_i; // bitwise copy or memberwise copy will suffer the same issue 
     // what else is there to do? 
     // BaseType has no knowledge of any other members 
    } 

    BaseType & BaseType::operator=(BaseType const & other) // copy assignment 
    { 
     m_i = other.m_i; 
     // what else is there to do? 
     // BaseType has no knowledge of any other members 
    } 
}; 

即使按位備份(std::is_trivially_copyable<T>),規模將是鹼基類型的,正如你指出的,比必要較小,並且將截斷數據。

希望這會有所幫助。

+0

是的,你剛纔描述了切片。我問技術原因爲什麼發生:) – 2014-10-16 22:03:35

+1

@AvivCohn這是技術原因。 Base的賦值運算符(或複製構造函數,如果您的示例有點不同)不知道Derived,也不應該將Derived的成員複製到自身中,或者以其他方式開始表現爲Derived。 – nos 2014-10-16 22:07:46

+0

那麼更準確的是:「發生切片是因爲已定義變量的大小已設置且無法更改,因此試圖放入大於其大小的任何東西都會導致切片」?或者:「類'A'的拷貝構造函數接受一個'A'類型的參數,所以它只是忽略了所有不是'A'類型的東西? – 2014-10-16 22:13:08

2

否在你的例子中,切片的原因是不同的。

在行Base b;中,您爲堆棧上的類型爲Base的對象分配空間並已調用其默認構造函數。因此,在您的if -statement的每個分支中,發生的是分配b,這通過賦值運算符實現,通常使用簽名Base::operator=(const Base&)。如果你不重載這個操作符,它的默認語義是一個逐字段拷貝。請注意,參數類型爲Base(或const Base&),因此只有右側的Base字段可見!

假設你存儲在Base對象包含在DerivedA對象的所有信息(雖然這是不可能的),你可以超載賦值運算符爲Base::operator=(const DerivedA&),實現自己的任務語義,上述的一些方法會工作得很好。

+0

我認爲這可以視爲切片(可以通過複製賦值運算符或通過複製構造函數來完成) – 2014-10-16 22:08:33

+0

是的,但是OP寫道:「所以我們決定總是對派生對象進行切片以適應基本類型的大小。 ,而觀察到的效果與自動大小截斷等沒有任何關係,而僅僅是爲了解決'DerivedA'實例的特性而未被重載的賦值'operator ='(正如我所說的,理論上它可以) 。我的理解是,這個問題不是關於是否切片,而是切片的原因是什麼。 – misberner 2014-10-16 22:15:13