2016-05-17 10 views
15

だからあなたが書くことができます直接割り当てを許可するshared_ptrません。なぜ<code>shared_ptr<Type></code>を使用しているとき

shared_ptr<Type> var(new Type()); 

を私は疑問に思う彼らは非常に単純で、より良い(IMO)を許可しなかった理由:

shared_ptr<Type> var = new Type(); 

代わりに、このような機能を実現するために、あなたは.reset()を使用する必要があります。

shared_ptr<Type> var; 
var.reset(new Type()); 

私が使用しています直接割り当て、すべてを可能にするスマートポインタが正常に動作しているOpenCVののPTRクラス

+3

ポインタを取る 'std :: shared_ptr'のコンストラクタは'明示的に 'であり、ポインタを取る'演算子= 'がないためです。 – Jarod42

+9

これは割り当てではありません。 – LogicStuff

答えて

20
生のポインタが暗黙のうちに std::shared_ptrに変換できるようにすることで問題は今

void foo(std::shared_ptr<int> bar) { /*do something, doesn't matter what*/ } 

int main() 
{ 
    int * bar = new int(10); 
    foo(bar); 
    std::cout << *bar; 
} 

場合で実証することができ

暗黙的な変換は、の終わりにbar点はshared_ptrデストラクタによって削除されることになるため、メモリを働い。 std::cout << *bar;でアクセスしようとすると、削除されたポインタを逆参照しているので、未定義の動作をするようになりました。それは問題ではないので、あなたの場合は

あなたは、コールサイトで直接ポインタを作成していますが例から見ることができるように、それは問題を引き起こす可能性があります。

8

なぜ[しない] shared_ptr許可直接割り当て[コピー初期設定]?

それはexplicitですので、herehereを参照してください。

私はそれの背後に根拠があるのだろうか?

TL(今除去コメントから); DR、任意コンストラクタ(またはキャスト)の製造explicitは、暗黙的な変換シーケンスに参加するのを防止することです。

explicitの要件がよりよく示されており、shared_ptr<>は関数の引数です。

void func(std::shared_ptr<Type> arg) 
{ 
    //... 
} 

と呼ばれます。

Type a; 
func(&a); 

これは、コンパイル、および書かれており、望ましくないと間違っているようです。期待どおりに動作しません。

ユーザー定義の(暗黙の)変換(キャスト演算子)をミックスに追加すると、さらに複雑になります。

struct Type { 
}; 

struct Type2 { 
    operator Type*() const { return nullptr; } 
}; 

そして、次の関数(明示的でない場合には)コンパイルが、恐ろしいバグを提供していますでしょう...

Type2 a; 
func(a); 
16

が、これは誤りがちである、あなたは直接ポインタ引数を持つ関数を呼び出すことができます許可なぜならあなたはコールサイトでそれから共有ポインタを作成しているということを必ずしも意識していないからです。

void f(std::shared_ptr<int> arg); 
int a; 
f(&a); // bug 

は、あなたがこれを無視した場合でも、あなたはコールサイトで目に見えない、一時的に作成し、shared_ptrを作成することは非常に高価です。

+1

'new'で作成しない限り、' delete'するメモリは 'new'ではありません。 – milleniumbug

+0

@giò、それは過負荷のあいまいさを引き起こす可能性があり、 'shared_ptr'を作ることは、暗黙のうちに大部分の人が望むよりも高価です。他のクラスでは、明白でない変換でもあります。例えば、 '5'を渡して' std :: vector'にすることは、非常に直感的ではありません。 – chris

28

構文:

shared_ptr<Type> var = new Type(); 

copy initializationです。これは、関数の引数に使用される初期化のタイプです。それが許された場合

、誤っスマートポインタを取る関数へのプレーンのポインタを渡すことができます。さらに、メンテナンス中に誰かがvoid foo(P*)void foo(std::shared_ptr<P>)に変更した場合、それはまったくコンパイルされず、結果として未定義の動作になります。

この操作は、本質的に、プレーンポインタの所有権を取得しているので、この操作は、明示的に行われなければなりません。このため、普通のポインタを取るコンストラクタは、偶発的な暗黙的な変換を避けるためにexplicitとなっています。


より安全で効率的な代替は、次のとおりです。

auto var = std::make_shared<Type>(); 
+4

@giò本当に大きな問題です。 –

+1

@giò共有ポインタは、ポインタを所有していることを前提としています(他の共有ポインタとのみ共有します)。生のポインタを共有ポインタに渡した場合、別のコードが未処理のポインタを所有していると仮定し、ある時点でそのポインタを削除しようとします。しかし、共有ポインタもそうです!だからええ、ダブル削除はまったく良いことではありません。 – KABoissonneault

8

彼らは非常に単純で、より良いことができなかった理由私はあなたの意見はあなたがより多くの経験を積んになるように変更して、もっとひどく書かれたバグのあるコードが発生します

...だろうか。

shared_ptr<>のように、すべての標準ライブラリオブジェクトは、未定義の動作(可能な限りすべての時間を浪費し、生きる意志を破壊するバグを見つけにくい)をできるだけ避けるように記述されています。

は考慮してください。

#include<memory> 

struct Foo {}; 

void do_something(std::shared_ptr<Foo> pfoo) 
{ 
    // ... some things 
} 

int main() 
{ 
    auto p = std::make_shared<Foo>(/* args */); 
    do_something(p.get()); 
    p.reset(); // BOOM! 
} 

このコードはコンパイルできません、それは良いことです。もしそうであれば、プログラムは未定義の動作を示すでしょう。私たちは二度同じFooのを削除すると思いますので、

です。

このプログラムはコンパイルされ、そしてよく形成されています。

#include<memory> 

struct Foo {}; 

void do_something(std::shared_ptr<Foo> pfoo) 
{ 
    // ... some things 
} 

int main() 
{ 
    auto p = std::make_shared<Foo>(/* args */); 
    do_something(p); 
    p.reset(); // OK 
} 
関連する問題