[英]Call default copy constructor from within overloaded copy constructor
我需要編寫一個復制構造函數來深度復制std::shared_ptr
的內容。 但是,有一堆變量int a, b, c, d, e;
類中也有定義。 有沒有辦法在我的新重載代碼中生成默認的復制構造函數代碼(或調用默認的復制構造函數)。
這是一個帶有注釋的代碼片段,希望能澄清這個問題。
class Foo {
public:
Foo() {}
Foo(Foo const & other);
...
private:
int a, b, c, d, e;
std::shared_ptr<Bla> p;
};
Foo::Foo(Foo const & other) {
p.reset(new Bla(*other.p));
// Can I avoid having to write the default copy constructor code below
a = other.a;
b = other.b;
c = other.c;
d = other.d;
e = other.e;
}
我一直認為像這樣的問題應該至少有一個來自標准的答案引用給未來的讀者,所以就在這里。
該標准的 §12.8.4 規定:
如果類定義未顯式聲明復制構造函數,則隱式聲明一個。
這意味着,當一個類定義並明確聲明拷貝構造函數,一個不隱含聲明。 所以如果你顯式聲明一個,隱式一個不存在,所以你不能調用它。
這是我寫這篇文章時的問題代碼:
class Foo {
public:
Foo() {}
Foo(Foo const & other);
...
private:
int a, b, c, d, e;
std::shared_ptr<Bla> p;
};
Foo::Foo(Foo const & other) {
p.reset(new Bla(other.p));
// Can I avoid having to write the default copy constructor code below
a = other.a;
b = other.b;
c = other.c;
d = other.d;
e = other.e;
}
上面的代碼很可能是錯誤的,因為
默認構造函數使a
、 b
、 c
、 d
和e
未初始化,並且
該代碼不負責作業復制,並且
表達式new Bla(other.p)
要求Bla
有一個采用std::shared_ptr<Bla>
的構造函數,這是極不可能的。
使用std::shared_ptr
這將必須是 C++11 代碼才能正式正確的語言。 但是,我相信它只是使用編譯器可用內容的代碼。 所以我認為相關的C++標准是C++98,加上C++03修正的技術修正。
即使在 C++98 中,您也可以輕松利用內置(生成的)復制初始化,例如
namespace detail {
struct AutoClonedBla {
std::shared_ptr<Bla> p;
AutoClonedBla( Bla* pNew ): p( pNew ) {}
AutoClonedBla( AutoClonedBla const& other )
: p( new Bla( *other.p ) )
{}
void swap( AutoClonedBla& other )
{
using std::swap;
swap( p, other.p );
}
AutoClonedBla& operator=( AutoClonedBla other )
{
other.swap( *this );
return *this;
}
};
}
class Foo {
public:
Foo(): a(), b(), c(), d(), e(), autoP( new Bla ) {}
// Copy constructor generated by compiler, OK.
private:
int a, b, c, d, e;
detail::AutoClonedBla autoP;
};
請注意,此代碼確實在默認構造函數中正確初始化,確實負責復制分配(為此使用交換習語),並且不需要特殊的智能指針感知Bla
構造函數,而是僅使用普通的Bla
復制構造函數復制。
在shared_ptr
上編寫一個內置深度復制的變shared_ptr
更容易。 這樣,您就不必為主類編寫復制構造函數; 僅針對這種特殊的deep_copy_shared_ptr
類型。 您的deep_copy_shared_ptr
將有一個復制構造函數,它本身會存儲一個shared_ptr
。 它甚至可以隱式轉換為shared_ptr
,以使其更易於使用。
那是不可能的。 要么您編寫自定義復制構造函數(完全由您自己編寫),要么編譯器為您編寫。
請注意,如果您編寫了一個復制構造函數,那么您可能還需要一個復制賦值和一個析構函數,因為編寫這三個資源管理函數中的任何一個都意味着您正在管理一個資源。 然而,使用復制和交換習語,您只需在復制構造函數中編寫一次復制邏輯,然后根據復制構造函數定義賦值運算符。
除此之外,我不完全確定您為什么使用shared_ptr<>
。 shared_ptr<>
在於允許多個指針安全地指向同一個對象。 但是您並沒有共享指針對象,而是深度復制了它。 也許您應該改用原始指針,並在析構函數中釋放它。 或者,更好的是,將shared_ptr<>
替換為clone_ptr
,然后完全消除復制構造函數、復制賦值和析構函數。
據我所知,您可以(並且應該)做的是使用初始化列表,無論如何:
Foo::Foo(Foo const & other)
: a(other.a), b(other.b), c(other.c),
d(other.d), e(other.e), p(new Bla(other.p))
{}
這不會使您免於寫作,但它會使您免於分配(不必要)默認構造的成員(盡管在這種情況下可能沒問題)可能帶來的性能損失以及這可能帶來的許多其他陷阱。 如果可能,請始終在構造函數中使用初始化列表。
順便說一下,Kerrek 的評論是正確的。 如果無論如何都要進行深層復制,為什么還需要shared_ptr
。 在這種情況下, unique_ptr
可能更合適。 除了它的好處shared_ptr
不是一個通用的無需再考慮解除分配的解決方案,您應該始終考慮是否需要智能指針以及哪種類型的智能指針最合適。
默認賦值運算符與默認復制構造函數具有相同的代碼。 盡管您不能從重載中調用默認的復制構造函數,但分配中存在相同的代碼。
Foo::Foo(Foo const & other)
{
*this = other;
p.reset(new Bla(other.p));
}
應該給你你需要的東西。
編輯:沒關系它實際上是相同的代碼,並且顯式聲明復制構造函數可以防止它生成:(
一個技巧是將那些“簡單”的字段放入基類
class FooBase {
protected:
int a, b, c, d, e;
};
class Foo : public FooBase {
public:
Foo() {}
Foo(const Foo& other)
: FooBase(other), // This copies all the simple fields
p(new Bla(other.p)) // Manually do the hard part
{}
...
private:
std::shared_ptr<Bla> p;
};
之后,如果您向 Foo 添加任何簡單字段,則可以將它們放入 FooBase,而無需擔心更新復制構造函數。
如果需要,不要忘記添加類似的復制賦值運算符。 在現代編譯器上,還添加了移動構造函數和移動賦值運算符。 這兩個可能是默認的,因為您不需要移動操作的深層復制。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.