Linux和Windows之间返回值差异的原因是什么以及如何解决?

时间:2016-07-06 15:58:15

标签: c++ linux visual-studio class gcc

这是我尝试返回类对象的代码。但是我从CentOs(gcc)和visual studio 2013获得了不同的结果。在cls.cpp中,使用gcc,它运行良好,我得到的结果如detector.name =" t_name",detector.stride = 5.但是探测器中的值是"" vs2013下0。似乎在vs2013中,cls对象被解构了。 为什么我得到不同的回报?以及如何使它在视觉工作室下运作良好?非常感谢。

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;
}

2 个答案:

答案 0 :(得分:3)

你有一个复制构造函数 nothing

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

GCC可能在cls tt = func();cls detector = builder->build();行中copy elision,它神奇地起作用。一旦启用优化,VS也可能会这样做。就个人而言,我说它是你代码中的一个错误。如果您有复制构造函数,则复制原始对象。

答案 1 :(得分:1)

我使用Visual Studio 2015尝试了你的代码,发现析构函数~cls()执行了两次。对我来说这看起来像个错误,但我不是专家。

通过&#34;解构&#34;,你的意思是&#34;被毁坏&#34;?

我编译使用Gnu g ++在Windows 10 64位下编译代码,在cygwin下编译。这次析构函数只运行一次。

我似乎记得当一个函数构造一个对象并返回它时(就像你的&#34; cls tt = func();&#34;),编译器可能会选择在最终位置构造对象,是,在调用函数的堆栈框架上。我不知道这种行为是否是强制性的。

现在我再试一次...在Visual Studio中,我在读取OUT的行处设置断点(&#34; ~cls()&#34;); (cls.cpp:12)。第一次触发断点时,堆栈跟踪为:

cls::~cls() (line 12)
func() (line 9, "return detector;")
main() (line 13, "cls tt = func();")

点击&#34;继续&#34;在调试器中,再次点击断点。现在堆栈是:

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

因此,似乎Visual Studio确实构造 - 并破坏 - func()堆栈框架中的对象,将对象复制到main()的堆栈框架,调用析构函数&#34; TT&#34;当func()退出时,当main()退出时,在第二个实例上再次调用析构函数。不要紧,赋值运算符是私有的,复制构造函数是一个空函数。

不幸的是,我不是专家,也不能说这与标准相比如何。

你说使用一个编译器&#34;检测器&#34;有价值&#34; t_name&#34;和5,但使用Visual Studio的值是&#34;&#34;和0.(你怎么知道?代码没有输出值,所以我猜你的代码中可能有更多的OUT ...)

我的猜测是编译器使用了空复制构造函数(cls.cpp第3行),因此main()堆栈框架中的副本(&#34; detector&#34;)没有数据复制到它。也许,这个教训是,你不能强迫Visual Studio只是通过拒绝它行为愚蠢来巧妙地表现。如果VS没有能力构造func()&#34; tt&#34;变量在main()&#39; s&#34;检测器&#34;变量,当你锁定赋值运算符时,它不会不情愿地获得这种能力。

也许我们都应该阅读何时使用赋值运算符,以及何时使用复制构造函数。看起来像Visual Studio解释你的代码

cls tt = func();
好像是

cls tt( func() );

使用复制构造函数初始化tt。也许仅当目标是已经正确初始化的对象时才使用赋值运算符。