[英]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.