这是我尝试返回类对象的代码。但是我从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;
}
答案 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。也许仅当目标是已经正确初始化的对象时才使用赋值运算符。