質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

4810閲覧

[C++]const参照にshared_ptrの右辺値を代入したときに参照カウンタが増加する理由

NUNU_E64

総合スコア63

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2017/12/27 09:57

C++のshared_ptrや左辺値/右辺値について勉強中です。

const参照で宣言した変数に、shared_ptrを返す関数で代入をした場合、参照カウンタが増加します。

これは参照ではなくコピーが発生しており、
const std::shared_ptr<Hoge>& x = foo.getHoge();

const std::shared_ptr<Hoge> x = foo.getHoge();
に差異はないということでしょうか?

右辺値(という認識)を参照に無理やり代入しようとすると、型変換が発生するということでしょうか。
shared_ptrの参照を返す関数で代入を行った場合は、参照カウンタは増加しません。

C++

1// hoge.h 2class Hoge { 3}; 4 5class Foo { 6public: 7 Foo() { 8 hoge = std::make_shared<Hoge>(); 9 } 10 std::shared_ptr<Hoge> getHoge() const { 11 return hoge; 12 } 13 14private: 15 std::shared_ptr<Hoge> hoge; 16}; 17

C++

1// hoge.cpp 2#include <iostream> 3#include "hoge.h" 4 5int main() { 6 Foo foo; // 参照カウンタ:0->1 7 8 std::cout << "foo.getHoge().use_count: " << foo.getHoge().use_count() << std::endl; // 2 9 10 { 11 const std::shared_ptr<Hoge>& x = foo.getHoge(); // 参照カウンタが増えている? 12 13 std::cout << "foo.getHoge().use_count: " << foo.getHoge().use_count() << std::endl; // 3 14 15 // 参照カウンタが減じている 16 } 17 18 std::cout << "foo.getHoge().use_count: " << foo.getHoge().use_count() << std::endl; // 2 19}

bash

1# 出力 2foo.getHoge().use_count: 2 3foo.getHoge().use_count: 3 4foo.getHoge().use_count: 2

なお、テストコードで、出力される参照カウンタ数が1大きいのは、
getHoge().use_count()を呼ぶ瞬間にコピーが発生して参照カウンタが増加しているからです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

こんにちは。

ご存知の通り、getHoge()はreturn文でコピーが発生しますね。
そのコピーにて生成されたstd::shared_ptr<Hoge>のインスタンスを 参照の方の x が拘束した結果、保持されます。x がスコープから外れる時に拘束していたstd::shared_ptr<Hoge>のインスタンスが一緒に開放されます。

参照でない方の x で保持した場合も振る舞いはほぼ同じですが、文法的には異なります。
最適化が働かない場合、getHoge()のreturn文でコピーされ一時オブジェクトが生成されます。次に x がコピーコンストラクタにて生成され、その後、その一時オブジェクトが破棄されます。
実際には RVO が働くので多くの処理系でコピーは1回で済む筈です。その結果、ほぼ同等の振る舞いのように見える筈です。

もし、std::shared_ptrとほぼ同じでコピー・コンストラクタのないクラスでやってみると、参照の x へ代入できても、非参照の x へは代入できない(コンパイルエラーになる)筈です。


【追記】
あっとと、ごめんなさい。コピー・コンストラクタはgetHoge()のreturn文でも使われるので、これをdeleteするとgetHoge()のreturn文でもエラーになります。なので参照の x への代入もできません。
う~ん、差を目に見えるようにするのがなかなか難しい。

投稿2017/12/27 10:24

編集2017/12/27 11:02
Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

NUNU_E64

2017/12/31 14:06 編集

わかりやすい回答ありがとうございます。 なるほど、参照を持つことによってreturn時の一時コピーが保持されるんですね。 文法上の差異についても理解できました。 メモ:一時オブジェクトの参照による束縛については下記も参考にしました http://ppp-lab.sakura.ne.jp/ProgrammingPlacePlus/cpp/language/017.html https://cpprefjp.github.io/lang/cpp11/rvalue_ref_and_move_semantics.html
guest

0

束縛による寿命を確かめるのであれば以下のようなコードだと思います。

c++

1#include <iostream> 2#include <memory> 3 4class Hoge { 5}; 6 7class Foo { 8public: 9 Foo() { 10 hoge = std::make_shared<Hoge>(); 11 } 12 std::shared_ptr<Hoge> getHoge() const { 13 return hoge; 14 } 15 const std::shared_ptr<Hoge>& ref() const { 16 return hoge; 17 } 18private: 19 std::shared_ptr<Hoge> hoge; 20}; 21 22int main(){ 23 Foo foo; // 参照カウンタ:0->1 24 25 std::cout << ".use_count: " << foo.ref().use_count() << std::endl; // 1 26 27 { 28 const std::shared_ptr<Hoge>&& x = foo.getHoge();//束縛されるので寿命が延びる 29 std::cout << "use_count:" << foo.ref().use_count() << std::endl;//2 30 } 31 { 32 foo.getHoge();//束縛されないので一時オブジェクトは解放される 33 std::cout << "use_count:" << foo.ref().use_count() << std::endl;//1 34 } 35 36 std::cout << "use_count: " << foo.ref().use_count() << std::endl; // 1 37}

投稿2017/12/27 13:08

hmmm

総合スコア818

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問