2016-07-06 7 views
0

ここでは、クラスのオブジェクトを返そうとしたコードを示します。 CentOS(gcc)やVisual Studio 2013とは異なる結果が得られました。cls.cppでは、gccを使用するとうまくいきますが、detect.name = "t_name"、detector.stride = 5.などの結果が得られますが、検出器の値は ""で、vs2013の下の値は0です。 vs2013ではclsオブジェクトが分解されているようです。 なぜ私は異なったリターンを得たのですか?それはビジュアルスタジオの下でうまくいくようにする方法は?どうもありがとう。LinuxとWindowsの間の戻り値の違いの原因とその修正方法は何ですか?

cls.h

#pragma once 
#include <iostream> 
#include <string> 
#define OUT(x) do{ std::cout << x << std::endl;}while(0) 
template <class T> class IBuilder{ 
public: 
    virtual ~IBuilder(){}; 
    virtual T build() = 0; 
}; 
class cls 
{ 
public: 
    ~cls(); 
    cls(const cls& origin); 
    class Builder : public IBuilder<cls>{ 
    private: 
     std::string _name; 
     int _stride = 4;   
     double _cascThr = -1; 
    public: 
     Builder(const std::string name); 
     ~Builder(); 
     Builder* stride(int s); 
     Builder* cascThr(double t); 
     cls build(); 
     friend cls; 
    }; 
private: 
    std::string _name; 
    int _stride; 
    double _cascThr; 
    Builder* _builder; 
    cls(Builder* builder); 
    cls& operator=(const cls&);//prevent the compiler to generate copying assignment 
}; 

cls.cpp

#include "cls.h" 
using namespace std; 
cls::cls(const cls& origin){} 
cls::cls(cls::Builder* builder) { 
    this->_builder = builder; 
    OUT("cls(Builder*)"); 
    this->_name = builder->_name; 
    this->_stride = builder->_stride; 
    this->_cascThr = builder->_cascThr; 
} 
cls:: ~cls(){ 
    OUT("~cls()"); 
} 
cls::Builder::Builder(const string name){ 
    OUT("Builder(string)"); 
    this->_name = name; 
} 
cls::Builder::~Builder(){ 
    OUT("~Builder() "); 
} 
cls::Builder* cls::Builder::stride(int s){ 
    this->_stride = s; 
    return this; 
} 
cls::Builder* cls::Builder::cascThr(double t){ 
    this->_cascThr = t; 
    return this; 
} 
cls cls::Builder::build(){ 
    OUT("Build ACF Detector From Builder"); 
    return cls(this); 
} 

main.cppに

#include "cls.h" 
using namespace std; 
cls func(){ 
    cls::Builder* builder = NULL; 
    builder = new cls::Builder("t_name"); 
    builder->stride(5); 
    builder->cascThr(1.0); 
    cls detector = builder->build(); 
    return detector; 
} 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
    cls tt = func(); // here I got different answers. 
    return 0; 
} 
+1

「異なる回答」とはどういう意味ですか?異なる*方法*? – nvoigt

+0

@nvoigtとgcc、 'tt.name =" t_name "'と 'tt.stride = 5'ですが、Visual Studioで' tt.name = ""と 'tt.stride = 0'を取得しました。 – wanglinski

答えて

3

あなたはコピーコンストラクタ目を持っていますで何もで。

cls::cls(const cls& origin){} 

GCCは、おそらくラインcls tt = func();cls detector = builder->build();copy elisionを行い、それが魔法のように動作します。たぶん、最適化を有効にしたら、VSもそれをやります。個人的には、あなたのコードのバグだと思います。コピーコンストラクタがある場合は、元のオブジェクトをコピーします。

+0

さらに、コピーコンストラクタはまったくありません。 – rubenvb

+0

これはバグだけでなく、RVO、コピーelisionが適用される必要がないので、現在はUBです(C++ 17はそれを変更します)。 – NathanOliver

+0

ええ、cls :: cls(const cls&origin)またはcls :: cls(const cls&origin)= defaultを削除すると、この問題は解決します。 – wanglinski

1

Visual Studio 2015を使用してコードを試したところ、デストラクタ〜cls()が2回実行されることがわかりました。それは私のバグのように見えますが、私は専門家ではありません。

「分解された」とは、「破壊された」という意味ですか?

cygwinの下でWindows 10 64ビット版でGnu g ++を使ってコードをコンパイルしました。今回は、デストラクタは一度だけ実行されました。

関数がオブジェクトを作成し、それを返すとき( "cls tt = func();"のように)、コンパイラはオブジェクトを最終的な場所、つまりスタック上に構築することを選択するかもしれないこの関数が必須であるかどうかわかりません

Visual Studioでは、OUT( "〜cls()"という行にブレークポイントを設定しています。 。。);(cls.cpp:12)ブレークポイントにヒットされて初めて、スタックトレースは次のとおりです。

再びブレークポイントを打つ、デバッガで「続ける」をクリック
cls::~cls() (line 12) 
func() (line 9, "return detector;") 
main() (line 13, "cls tt = func();") 

今、スタックは次のとおりです。

cls::~cls() (line 12) 
main() (line 14, "return 0"). 

だから、Visual Studioが構築しないようだ - と破壊 - FUNC内のオブジェクト()のスタックフレーム、()の主にオブジェクトをコピーのスタックフレーム、TT」にデストラクタを呼び出します"func()は終了し、main()が終了すると2番目のインスタンスでデストラクタを再度呼び出します。代入演算子がprivateであることに気をつけてください。コピーコンストラクタは空の関数です。

残念ながら、私は専門家ではなく、これが標準とどのように比較されているかは言えません。

"detector"は "t_name"と5の値を持っていますが、Visual Studioを使うと値は ""と0です。コードは値を出力しないので、あなたのコードにもっと多くのOUTがあるかもしれないと思います)。

私の推測では、コンパイラは空のコピーコンストラクタ(cls.cpp 3行目) main()のスタックフレーム( "検出器")にはデータがコピーされていませんでした。教訓は、おそらく、愚かな振る舞いを否定するだけで、Visual Studioに巧みな振る舞いを強制することはできません。 VSがmain()の "detect"変数の中にfunc()の "tt"変数を構築する能力を持たない場合、代入演算子をロックダウンしたときに、この機能を不本意に取得することはありません。

おそらく、代入演算子が正確に使用されたとき、およびコピーコンストラクタが使用されているときには、両方とも読み上げる必要があります。それはTTを初期化するために、コピーコンストラクタを使用して

cls tt(func()); 

であるかのようにVisual Studioがあなたのコードが

cls tt = func(); 

を解釈するように思えます。おそらく、代入演算子は、宛先が既に適切に初期化されているオブジェクトである場合にのみ使用されます。

+0

ありがとう!私はデバッグ中に値を得ました。あなたの分析は本当に役に立ちます:) – wanglinski

関連する問題