はじめに
std::vector に独自クラスを入れる場合、コピーコンストラクタとムーブコンストラクタのいずれかが必要になります。
コピーコンストラクタについては、このページに詳しく書かれています。
http://nobunaga.hatenablog.jp/entry/2016/07/03/230337
コード例
以下にサンプルコードを記載します。
#include <iostream>
#include <vector>
class Ball {
private:
int m_radius;
public:
Ball(int someRadius) : m_radius(someRadius)
{
}
int getRadius(){return m_radius;}
int getDiameter(){return m_radius * 2;}
Ball (const Ball& rhs) : m_radius(rhs.m_radius)
{
}
};
int main(void){
std::vector<Ball> v;
v.push_back(Ball(3));
v.push_back(Ball(5));
v.push_back(Ball(6));
for(auto item : v)
{
std::cout << "Radius : " << item.getRadius() << ", Diameter : " << item.getDiameter() << std::endl;
}
}
Radius : 3, Diameter : 6
Radius : 5, Diameter : 10
Radius : 6, Diameter : 12
vector::push_backのテスト
次に、独自クラスをvector::push_backを使ってstd::vectorに格納した場合、コンストラクタとデストラクタ、コピーコンストラクタがどの程度呼び出されているか調べてみました。
#include <iostream>
#include <vector>
class Ball {
private:
int m_radius;
public:
Ball();
Ball(int someRadius) : m_radius(someRadius)
{
std::cout << "Call Constructor (Radius : " << someRadius << ")" << std::endl;
}
~Ball()
{
}
int getRadius(){return m_radius;}
int getDiameter(){return m_radius * 2;}
Ball (const Ball& rhs) : m_radius(rhs.m_radius)
{
std::cout << "Call Copy Constructor (Radius : " << m_radius << ")" << std::endl;
}
};
int main(void){
std::vector<Ball> v;
v.push_back(Ball(1));
v.push_back(Ball(2));
v.push_back(Ball(3));
v.push_back(Ball(4));
v.push_back(Ball(5));
}
Call Constructor (Radius : 1)
Call Copy Constructor (Radius : 1)
Call Constructor (Radius : 2)
Call Copy Constructor (Radius : 2)
Call Copy Constructor (Radius : 1)
Call Constructor (Radius : 3)
Call Copy Constructor (Radius : 3)
Call Copy Constructor (Radius : 1)
Call Copy Constructor (Radius : 2)
Call Constructor (Radius : 4)
Call Copy Constructor (Radius : 4)
Call Constructor (Radius : 5)
Call Copy Constructor (Radius : 5)
Call Copy Constructor (Radius : 1)
Call Copy Constructor (Radius : 2)
Call Copy Constructor (Radius : 3)
Call Copy Constructor (Radius : 4)
vector::push_backに、ムーブコンストラクタを使用してみる
上記のコードで、Ballクラスにムーブコンストラクタを使用しました。
ムーブコンストラクタには、noexcept をつけましょう。(参考リンク参照)
noexcept修飾子をつけないと、std::vector の push_back 時にコピーコンストラクタが呼ばれてしまいます。(本コードでも確認済み)
理由
std::vector は push_back の内部で std::move_if_noexcept() を使って要素を移動している。
この std::move_if_noexcept() がどのような動作をするかというと、
ムーブコンストラクタに noexcept 修飾子を付がついている(=「このコンストラクタは例外を投げないよ」とコンパイラに明示している)ときには右辺値参照&&をリターン
noexcept 修飾子がついていないときは const 左辺値参照をリターン
#include <iostream>
#include <vector>
class Ball {
private:
int m_radius;
public:
Ball();
Ball(int someRadius) : m_radius(someRadius)
{
std::cout << "Call Constructor (Radius : " << someRadius << ")" << std::endl;
}
~Ball()
{
}
int getRadius(){return m_radius;}
int getDiameter(){return m_radius * 2;}
Ball (const Ball& rhs) : m_radius(rhs.m_radius)
{
std::cout << "Call Copy Constructor (Radius : " << m_radius << ")" << std::endl;
}
Ball(Ball&& rhs) noexcept : m_radius(rhs.m_radius)
{
std::cout << "Call Move Constructor (Radius : " << m_radius << ")" << std::endl;
}
};
int main(void){
std::vector<Ball> v;
v.push_back(Ball(1));
v.push_back(Ball(2));
v.push_back(Ball(3));
v.push_back(Ball(4));
v.push_back(Ball(5));
}
Call Constructor (Radius : 1)
Call Move Constructor (Radius : 1)
Call Constructor (Radius : 2)
Call Move Constructor (Radius : 2)
Call Move Constructor (Radius : 1)
Call Constructor (Radius : 3)
Call Move Constructor (Radius : 3)
Call Move Constructor (Radius : 1)
Call Move Constructor (Radius : 2)
Call Constructor (Radius : 4)
Call Move Constructor (Radius : 4)
Call Constructor (Radius : 5)
Call Move Constructor (Radius : 5)
Call Move Constructor (Radius : 1)
Call Move Constructor (Radius : 2)
Call Move Constructor (Radius : 3)
Call Move Constructor (Radius : 4)
参考リンク