[英]Handling smart pointers in stl container
我有一個類Foo<T>
,它有一個指向Shape
派生類的智能指針。 我正在嘗試實現at(index)
成員函數。 這是我要直觀地做的事情:
Foo<float> myfoo;
std::unique_ptr<Shape<float>> shape_ptr = myfoo.at(i);
shape_ptr->doSomething(param1, param2, ...);
在定義at(index)
函數時,我收到編譯器錯誤消息。 請注意,定義了移動構造函數,並且Shape基類是抽象的。 下面,我將提供一些代碼用於說明目的。
此外,我最近在網上找到了一個關於如何使用std::move
重載賦值運算符的示例。 我通常遵循Copy-Swap習語。 重載上述操作符的兩種方法中的哪一種對我的情況有意義? 下面,我還說明了函數的定義。
template < typename T >
class Foo{
public:
Foo();
Foo( Foo && );
~Foo();
void swap(Foo<T> &);
//Foo<T> & operator =( Foo<T> );
Foo<T> & operator =( Foo<T> && );
std::unique_ptr<Shape<T> > at ( int ) const; // error here!
int size() const;
private:
std::vector< std::unique_ptr<Shape<T> > > m_Bank;
};
template < typename T >
Foo<T>::Foo( Foo && other)
:m_Bank(std::move(other.m_Bank))
{
}
/*template < typename T >
void Filterbank<T>::swap(Filterbank<T> & refBank ){
using std::swap;
swap(m_Bank, refBank.m_Bank);
}
template < typename T >
Foo<T> & Filterbank<T>::operator =( Foo<T> bank ){
bank.swap(*this);
return (*this);
}*/
template < typename T >
Foo<T> & Foo<T>::operator =( Foo<T> && bank ){
//bank.swap(*this);
m_Bank = std::move(bank.m_Bank);
return (*this);
}
template < typename T >
std::unique_ptr<Shape<T> > Foo<T>::at( int index ) const{
return m_Bank[index]; // Error here! => error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'
}
使用Boost的指針容器而不是帶unique_ptr的標准容器。 它們是為這種用途而設計的。
我認為你應該在這里使用shared_ptr 。
只有一個unique_ptr可以擁有共享資源。 如果你能夠做你想做的事情,即按值返回unique_ptr那么向量中的那個將被銷毀 ,這可能是你不想要的。
Q1:如何處理Foo::at( int ) const
,以便您可以:
myfoo.at(i)->doSomething(param1, param2, ...);
不將所有權轉移出vector<unique_ptr<Shape<T>>>
。
A1: Foo::at( int ) const
應該返回一個const std::unique_ptr<Shape<T> >&
:
template < typename T >
const std::unique_ptr<Shape<T> >&
Foo<T>::at( int index ) const
{
return m_Bank[index];
}
現在你可以取消引用const unique_ptr
並調用他們想要的任何Shape
成員(const或非const)。 如果他們不小心嘗試復制unique_ptr
(它會將所有權轉移出Foo
),他們將收到編譯時錯誤。
這個解決方案比將非const引用返回到unique_ptr
要好,因為它會捕獲Foo
意外所有權轉移。 但是,如果您希望允許從Foo
通過at
進行所有權轉移,那么非const引用將更合適。
Q2:此外,我最近在網上找到了一個關於如何使用std :: move重載賦值運算符的示例。 我通常遵循Copy-Swap習語。 重載上述操作符的兩種方法中的哪一種對我的情況有意義?
A2:我不確定~Foo()
作用。 如果它沒有做任何事情,你可以刪除它,然后(假設完全符合C ++ 11)你將自動獲得正確和最佳的移動構造函數和移動賦值運算符(以及正確刪除的復制語義)。
如果你不能刪除~Foo()
(因為它做了一些重要的事情),或者你的編譯器還沒有實現自動移動生成,你可以明確地提供它們,就像你在問題中所做的那樣。
你的移動構造函數是現場:移動構造成員。
您的移動分配應該類似(並且如果~Foo()
是隱式的,則會自動生成):移動分配成員:
template < typename T >
Foo<T> & Foo<T>::operator =( Foo<T> && bank )
{
m_Bank = std::move(bank.m_Bank);
return (*this);
}
你的Foo
設計也適用於Swappable
,這總是很好供應:
friend void swap(Foo& x, Foo& y) {x.m_Bank.swap(y.m_Bank);}
如果沒有這種明確的swap
,你Foo
還是Swappable
使用Foo
的舉動構造函數和移動賦值。 然而,這種顯式swap
速度大約是隱式swap
的兩倍。
以上建議都旨在獲得Foo
最高性能。 如果需要,您可以在移動作業中使用復制交換習語。 這將是正確的,稍慢。 雖然如果你小心你沒有通過swap
調用移動賦值和移動賦值調用swap
獲得無限遞歸! :-)確實,這個問題只是干凈(和最佳)分離swap
和移動分配的另一個原因。
更新
假設Shape
看起來像這樣 ,這里有一種方法可以為Foo
編寫移動構造函數,移動賦值,復制構造函數和復制賦值運算符,假設Foo
有一個數據成員:
std::vector< std::unique_ptr< Shape > > m_Bank;
...
Foo::Foo(Foo&& other)
: m_Bank(std::move(other.m_Bank))
{
}
Foo::Foo(const Foo& other)
{
for (const auto& p: other.m_Bank)
m_Bank.push_back(std::unique_ptr< Shape >(p ? p->clone() : nullptr));
}
Foo&
Foo::operator=(Foo&& other)
{
m_Bank = std::move(other.m_Bank);
return (*this);
}
Foo&
Foo::operator=(const Foo& other)
{
if (this != &other)
{
m_Bank.clear();
for (const auto& p: other.m_Bank)
m_Bank.push_back(std::unique_ptr< Shape >(p ? p->clone() : nullptr));
}
return (*this);
}
如果您的編譯器支持默認的移動成員,則可以通過以下方式實現同樣的目的:
Foo(Foo&&) = default;
Foo& operator=(Foo&&) = default;
用於移動構造函數和移動賦值運算符。
以上確保每個Shape
始終只由一個智能指針/向量/ Foo擁有。 如果您希望多個Foo
共享Shape
的所有權,那么您可以擁有作為您的數據成員:
std::vector< std::shared_ptr< Shape > > m_Bank;
並且您可以默認所有移動構造函數,移動賦值,復制構造函數和復制賦值。
看起來你應該在這里返回一個引用:
Shape<T> & Foo<T>::at( int index ) const{
return *m_Bank[index];
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.