2013-05-07 75 views
13

當我們將派生類的對象分配或複製到其基類的對象時,會發生對象切片,在進程中丟失派生類的派生部分。對象切片有用嗎?

這裏已經有更深入的解釋:What is the slicing problem in C++?

(我自己,我不認爲這是一個問題,而語言的值語義的自然結果,但這不是這個問題的地步。)

我想知道的是:是有沒有什麼情況下你會專門使用它?它是「工作的正確工具」的情況?

+1

可能重複: http://stackoverflow.com/questions/2389125/object-slicing-is-it-advantage – shivakumar 2013-05-07 10:17:22

+0

@shivakumar感謝,很高興的發現。儘管如此,答案中的例子已經完成。我希望有人能拿出真實世界的例子。 – jrok 2013-05-07 10:32:33

回答

5

當然,當想要放棄類的派生部分時,可能會很有用,也許會放棄依賴關係。

例如,假設我們有一個對象系統設置,其中每個基類都屬於一個派生類型,並且每個派生類型都有不同的依賴關係,可能通過依賴注入來實現。可能需要創建基地的克隆,但可能需要爲該基地的實際派生類型分配一組全新的依賴關係。

這可以比喻爲一個遊戲引擎,其中有許多類型的對撞機。每個碰撞器以各種方式從基礎接口類對象派生。我們想克隆一個碰撞器來檢索它的位置和比例(從底部),但是想要在這個底部上放置一個完全不同的派生實現。 「對象切片」可能是實現此目的的一種簡單方法。

實際上,一個組件或聚合對象組織比專門對象切片更有意義,但它大多是相同的想法。

+0

從概念上講,對於任何給定的類型來說,繼承相關的可替換性似乎通常對於可變間接的一個特定深度通常是有意義的。 Java和.NET假設除了數組可替換性適用於單獨 - 間接引用(引用「派生」替代引用「基本」,但是例如「列表」不替代「列表」)。我認爲主要的切片「問題」源自這樣一個事實,即直接可替代的類型不應允許可變引用的單一間接替換。 – supercat 2013-08-12 16:58:32

0

有些STL實現實際使用對象切片來實現算法: 例如,使用iterator_tags 你可以很容易地std::advance使用最高效的算法:

namespace std { 

template <class I> 
void advance_impl(I& it, int n, input_iterator_tag) { 
    for (; n > 0; --n) 
     ++it; 
} 

// Forward Iterators use the same implementation as Input Iterators... 

// TODO: 
// Add bidirectional_iterator_tag implementation... 

template <class I> 
void advance_impl(I& it, int n, random_access_iterator_tag) { 
    it += n; 
} 

template <class I> 
void advance(I& it, int n) { 
    advance_impl(it, n, typename iterator_traits<I>::iterator_category()); 
} 

} // std 

使用自己的小類層次結構可以消除歧義,否則曖昧函數重載。例如。要將對象轉換爲std::string,您可能需要使用對象成員函數to_string()(如果它存在或以其他方式使用operator<<)。

struct R2 {};  // rank 2 
struct R1 : R2 {}; // rank 1 

// C++11. 
// Use some type traits and enable_if in C++03. 
template <class T> 
auto ToString(R1, T const& t) -> decltype(t.to_string()) { 
    return t.to_string(); 
} 

template <class T> 
std::string ToString(R2, T const& t) { 
    std::ostringstream s; 
    s << t; 
    return s.str(); 
} 

template <class T> 
std::string ToString(T const& t) { 
    return ToString(R1(), t); 
}