簡體   English   中英

如何確保只調用一次虛擬基類上的賦值運算符?

[英]How to ensure that the assignment operator on a virtual base class is called only once?

我正在使用虛擬繼承,如典型的鑽石問題:

            A
(virtual) /   \ (virtual)
        B       C
          \   /
            D

我正在每個類中實現一個名為“deep_copy_from”的方法(但它也可以是賦值運算符=())。 該方法應復制類自己的屬性,並將副本傳播到上面的類。

問題是當我深度復制D實例時,A :: deep_copy_from方法被調用兩次(並且它應該只被調用一次,因為只有一個“版本”的A)。 確保只調用一次的最佳方法是什么?

(B :: deep_copy_from和C :: deep_copy_from應該繼續以相同的方式工作)。

這是一個示例代碼:

class A
{
public:
    A(string const& p_a_name) : a_name(p_a_name) {
        cout << "A(a_name=\"" << p_a_name << "\")" << endl;
    }

    virtual void deep_copy_from(A const& a)
    {
        cout << "A::deep_copy_from(A(a_name=\"" << a.a_name << "\"))" << endl;
        this->a_name = a.a_name;
    }

protected:
    string a_name;
};

class B : public virtual A
{
public:
    B(string const &p_a_name, string const& p_b_name) : A(p_a_name), b_name(p_b_name) {
        cout << "B(a_name=\"" << p_a_name << "\", b_name=\"" << p_b_name << "\")" << endl;
    }

    virtual void deep_copy_from(B const& b)
    {
        cout << "B::deep_copy_from(B(a_name=\"" << b.a_name << "\", b_name=\"" << b.b_name << "\"))" << endl;
        this->A::deep_copy_from(static_cast<A const&>(b));
        this->b_name = b.b_name;
    }

protected:
    string b_name;
};

class C : public virtual A
{
public:
    C(string const &p_a_name, string const& p_c_name) : A(p_a_name), c_name(p_c_name) {
        cout << "C(a_name=\"" << p_a_name << "\", c_name=\"" << p_c_name << "\")" << endl;
    }

    virtual void deep_copy_from(C const& c)
    {
        cout << "C::deep_copy_from(C(a_name=\"" << c.a_name << "\", c_name=\"" << c.c_name << "\"))" << endl;
        this->A::deep_copy_from(static_cast<A const&>(c));
        this->c_name = c.c_name;
    }

protected:
    string c_name;
};

class D : public B, public C
{
public:
    D(string const &p_a_name, string const& p_b_name, string const& p_c_name, string const& p_d_name)
        : A(p_a_name), B(p_a_name, p_b_name), C(p_a_name, p_c_name), d_name(p_d_name)
    {
        cout << "D(a_name=\"" << p_a_name << "\", b_name=\"" << p_b_name
             << "\", c_name=\"" << p_c_name << "\", d_name=\"" << p_d_name << "\")" << endl;
    }

    virtual void deep_copy_from(D const& d)
    {
        cout << "D::deep_copy_from(D(a_name=\"" << d.a_name << "\", b_name=\"" << d.b_name
            << "\", c_name=\"" << d.c_name << "\", d_name=\"" << d.d_name << "\"))" << endl;
        this->B::deep_copy_from(static_cast<B const&>(d));
        this->C::deep_copy_from(static_cast<C const&>(d));
        this->d_name = d.d_name;
    }

protected:
    string d_name;
};

這是當前的輸出:

A(a_name="A")
B(a_name="A", b_name="B")
C(a_name="A", c_name="C")
D(a_name="A", b_name="B", c_name="C", d_name="D")
D::deep_copy_from(D(a_name="A", b_name="B", c_name="C", d_name="D"))
B::deep_copy_from(B(a_name="A", b_name="B"))
A::deep_copy_from(A(a_name="A"))
C::deep_copy_from(C(a_name="A", c_name="C"))
A::deep_copy_from(A(a_name="A"))

更新:

目前的版本現在是:

class A
{
public:
    A(string const& p_a_name) : a_name(p_a_name) {
        cout << "A(a_name=\"" << p_a_name << "\")" << endl;
    }

    virtual void deep_copy_from(A const& a)
    {
        cout << "A::deep_copy_from(A(a_name=\"" << a.a_name << "\"))" << endl;
        this->a_name = a.a_name;
    }

protected:
    string a_name;
};

class B : public virtual A
{
public:
    B(string const &p_a_name, string const& p_b_name) : A(p_a_name), b_name(p_b_name) {
        cout << "B(a_name=\"" << p_a_name << "\", b_name=\"" << p_b_name << "\")" << endl;
    }

    virtual void deep_copy_from(B const& b)
    {
        cout << "B::deep_copy_from(B(a_name=\"" << b.a_name << "\", b_name=\"" << b.b_name << "\"))" << endl;
        this->A::deep_copy_from(static_cast<A const&>(b));
        this->deep_copy_my_bits_from(b);
    }

protected:
    void deep_copy_my_bits_from(B const& b) {
        cout << "B::deep_copy_my_bits_from(B(a_name=\"" << b.a_name << "\", b_name=\"" << b.b_name << "\"))" << endl;
        this->b_name = b.b_name;
    }

protected:
    string b_name;
};

class C : public virtual A
{
public:
    C(string const &p_a_name, string const& p_c_name) : A(p_a_name), c_name(p_c_name) {
        cout << "C(a_name=\"" << p_a_name << "\", c_name=\"" << p_c_name << "\")" << endl;
    }

    virtual void deep_copy_from(C const& c)
    {
        cout << "C::deep_copy_from(C(a_name=\"" << c.a_name << "\", c_name=\"" << c.c_name << "\"))" << endl;
        this->A::deep_copy_from(static_cast<A const&>(c));
        this->deep_copy_my_bits_from(c);
    }

protected:
    void deep_copy_my_bits_from(C const& c) {
        cout << "C::deep_copy_my_bits_from(C(a_name=\"" << c.a_name << "\", c_name=\"" << c.c_name << "\"))" << endl;
        this->c_name = c.c_name;
    }

protected:
    string c_name;
};

class D : public B, public C
{
public:
    D(string const &p_a_name, string const& p_b_name, string const& p_c_name, string const& p_d_name)
        : A(p_a_name), B(p_a_name, p_b_name), C(p_a_name, p_c_name), d_name(p_d_name)
    {
        cout << "D(a_name=\"" << p_a_name << "\", b_name=\"" << p_b_name
             << "\", c_name=\"" << p_c_name << "\", d_name=\"" << p_d_name << "\")" << endl;
    }

    virtual void deep_copy_from(D const& d)
    {
        cout << "D::deep_copy_from(D(a_name=\"" << d.a_name << "\", b_name=\"" << d.b_name
            << "\", c_name=\"" << d.c_name << "\", d_name=\"" << d.d_name << "\"))" << endl;
        this->A::deep_copy_from(static_cast<A const&>(d));
        this->B::deep_copy_my_bits_from(static_cast<B const&>(d));
        this->C::deep_copy_my_bits_from(static_cast<C const&>(d));
        this->d_name = d.d_name;
    }

protected:
    string d_name;
};

輸出是:

A(a_name="A")
B(a_name="A", b_name="B")
C(a_name="A", c_name="C")
D(a_name="A", b_name="B", c_name="C", d_name="D")
D::deep_copy_from(D(a_name="A", b_name="B", c_name="C", d_name="D"))
A::deep_copy_from(A(a_name="A"))
B::deep_copy_my_bits_from(B(a_name="A", b_name="B"))
C::deep_copy_my_bits_from(C(a_name="A", c_name="C"))

我能得到比這更好的東西嗎? (即更自動化)

@Alf對於任務是正確的:任務被搞砸了。 原因是它是一個二元操作,由於協方差問題,不可能在OO框架中調度二進制操作。

現在,您的問題有一個通用答案,但首先您需要了解兩件事。 首先,虛擬基礎總是公開的,無論你聲明什么,無論標准說什么:標准都是錯誤的。 [證明:只需派生另一個類並再次聲明任何虛擬基礎公共虛擬,並且您有權訪問]

第二個事實是虛擬基礎是它們間接基礎的每個類別的直接基礎。 再次,忽略標准,因為它是錯誤的。 往上看。

鑒於這兩個事實,很容易看到正確的模式,以避免重復:

這是你的鑽石:

struct A { cp(){ "A" } virtual CP(){ cp(); } };
struct B : virtual A { cp(){ "B" } CP() { cp(); A::CP(); } };
struct C : ... ibid ...
struct D : B, C, virtual A { 
   cp() { "D"; B::cp(); C::cp(); }
   CP() { cp(); A::cp(); }
};

為了簡潔,我放棄了返回類型和其他東西。 cp()函數通過首先處理任何成員然后調用每個非虛擬基礎來處理其成員(遞歸地)來向下鑽取。 實際上它應該受到保護,因為它不適合公共客戶。 向下鑽取是強制性的,因為您無法自己訪問間接非虛擬基礎,只能直接訪問。

CP()函數是虛擬的,因此無論您使用哪個指針(A,B,C或D),任何調用都會轉到完整對象的唯一CP。

它通過調用自己的類的cp()處理所有成員和非虛擬基礎子對象成員,然后處理虛擬基礎,在這種情況下只有一個,即A.

如果X :: CP()成為

X *X::clone() const;

然后,如果你可以從任何指針克隆完整的對象並返回相同的動態和靜態類型:如果你的動態類型是D而靜態類型是B,你將得到一個B *到D對象,就像你開始時一樣。

不可能以這種方式進行分配。 根本不可能完成作業。 原因是賦值在兩個參數上是協變的。 無法確保源和目標具有相同的動態類型,這是分配工作所必需的。 如果源太大,其中一些會被切掉。 更糟糕的是,如果目標太大,其中一些永遠不會被分配給。 因此,您調度的對象(目標或源)沒有區別:它無法正常工作。 唯一可以工作的分配是基於靜態類型的非虛擬分配。 這也可以在片段之上或之下,但至少這個問題是非常明顯的。

克隆有效,因為它是一個只有一個參數的函數(即自我對象)。 通常,如果你使用“對象東西”而不是“有價值的東西”那么,因為你只能真正操縱值,你必須使用指針。 在這種情況下,clone()和朋友就是你想要的:你可以指定一個指針就好了!

有兩個問題:一個是關於雙重復制到A部分,另一個是關於虛擬賦值操作。

虛擬分配:不是一個好主意,因為它將錯誤檢測從編譯時間轉移到運行時。 根本就不要。 似乎需要虛擬分配(或類似於分配的操作)的一般解決方案是實現克隆,即生成動態分配副本的虛擬clone成員函數。

雙重復制:簡單的答案是在結構方面表達任務。 這樣做的慣用方法被稱為“交換習語”。 簡單地說, 構造一個副本,然后其內容與當前實例交換 ,然后讓你構造的實例的析構函數進行清理。

干杯&hth。,

deep_copy_from不是共變體,您只能在返回類型中使用協方差。

您可能會使用您編寫的代碼獲得“過載隱藏虛擬功能”警告。

因為在沒有調用A版本的情況下無法調用B或C版本,所以除非您修改B或C,否則如果必須同時調用B和C版本,則無法避免A版本被調用兩次。

鑒於B和C都從A繼承虛擬,你可能不應該讓它們調用A版本,因為最終的類負責A部分。

暫無
暫無

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

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