簡體   English   中英

具有指向其父級的指針的對象是否應定義了復制構造函數?

[英]Should an object with a pointer to its parent have a copy constructor defined?

如果對象A包含成員對象B並且對象B具有指向其父對象A的指針,是否需要為對象B指定復制構造函數?

假設沒有動態分配。

此外,3規則適用於此嗎?

您的設計使用雙向導航實現合成。 這完全有效。
然而,正如謝爾蓋在評論中指出的那樣,這樣的設計並非沒有問題。

假設您有一個Object類和一個包含Object的類Container 這里有一些基本問題:

Container c;
Object mo1; // Q1: should this be valid ? (i.e. is an object without parent allowed 
Object mo2 = c.o;  // Q2: is this acceptable ?  Q3: Who is parent of mo2 ?  

看看問題Q2和Q3:如果這樣的初始化是可以接受的,那么直接的問題是你想要的父母:

  • 如果mo2應該沒有父級,則需要根據3的規則使用復制構造函數來清除父級。
  • 如果mo2應引用同一個父級(盡管它不是成員),則可以保留默認的復制構造函數。

例:

struct Container; 
struct Object{
    Container *parent;  
    Object (Container *p=nullptr) : parent(p) { cout << "construct object"<<endl; }
    // copy constructor or compiler generated one depending on Q2+Q3
    ~Object() {}
    ...
};
struct Container {
    Object o;
    Container() : o(this) {}
};

如果不能接受這樣的初始化,則應該在代碼中明確禁止復制構造。

Object (const Object &o) = delete;  

重要說明: Container可能還需要一個復制構造函數。 無論您決定使用Object ,如果必須是可復制的,您可能必須在Container處理。 你決不能在那里使用Object的拷貝構造函數。

重要說明2: Container本身可用於更復雜的情況。 vector<Container>為例。 將新容器添加到向量時,可能會執行重定位,如果您沒有提供注意的Container復制構造函數,則可能會使指針無效!

是的,你應該考慮實現一個復制構造函數,即使子對象沒有管理任何句柄或指向資源的指針(如果沒有這樣的構造函數會導致你清楚理解的熟悉的問題,它將與副本盲目地共享)。

這樣做的原因是父母的孩子的副本不一定被認為是父母的孩子!

如果孩子的副本被認為是在同一個父母的同一個,並且父母有一個孩子的列表,他們的責任是插入該列表? 它必須在某個地方完成。 如果它沒有在復制構造函數中完成,那么代碼中執行復制的每個位置都必須記住采取一些額外的操作,例如copy.register_with_parent() 任何遺忘的地方很可能都是一個錯誤。

如果不認為子項的副本在同一父項下,則無論如何,一個天真的成員成員副本將具有父指針。 使用指向父實際上不是父代的指針的對象是什么? 有些東西必須清除指針,或將其設置為某些“所有孤立子對象的默認父對象”或其他什么。 這將在復制構造函數中完成。

如果您不知道這些問題的答案,那么請定義一個未實現的復制構造函數,以便在您確實知道該行為應該是什么之前不會進行復制。 所以,再次復制構造函數,雖然聲明但未定義!

哦,但除非用指針分配,否則父對象不能擁有它的被動類對象:

class B;

class A
{
    B son;
};

class B : public A
{
    A* parent;
};

結果:

field 'son' has incomplete type 'B'
     B son;
       ^

然后,如果你用分配做到了:

class B;

class A
{
public:
    A(void);
    ~A();
private:
    B* son;
};

class B : public A
{
public:
    B() {} // <-depends what you want to archieve
private:
    A* parent;
};

A::A(void) : son(new B) {}
A::~A() {delete son;}

此時出現了問題。 您希望父指針指向全局對象A ,分配它自己的A對象,還是指向this

全局變量 - 無需編寫復制構造函數:

// A class not changed
A global;
class B : public A
{
public:
    B() {parent = &global;}
...

如果object分配了它自己的A對象,那么你需要堅持3(或4)的規則。 更重要的是,你的A類也需要堅持這個規則,因為我們將復制父對象,所以每個B對象都有它自己的:

class B;

class A
{
public:
    A(void);
    A(const A& other);
    A& operator=(const A& other);
    ~A();
private:
    B* son;
};

class B : public A
{
public:
    B() : parent(new A) {}
    B(const B& other) : parent(new A(*other.parent)) {}
    B& operator=(const B& other);
    ~B() {delete parent;}
private:
    A* parent;
};

A::A(void) : son(new B) {}
A::A(const A& other) : son(new B(*other.son)) {}
A& A::operator=(const A& other)
{
    delete son;
    son = new B(*other.son);
    return *this;
}

B& B::operator=(const B& other)
{
    delete parent;
    parent = new A(*other.parent);
    return *this;
}

A::~A() {delete son;}

最后,對象B指向同一個對象基類,不需要像全局變量那樣特殊的東西:

class B;

class A
{
public:
    A(void);
    ~A();
private:
    B* son;
};

class B : public A
{
public:
    B() : parent(this) {} // pointless, but yeah, nothing else is needed.
private:
    A* parent;
};

A::A(void) : son(new B) {}
A::~A() {delete son;}

這不是應該做什么,不應該做什么。 這一切都取決於你想要達到的目標。

暫無
暫無

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

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