Linux和Windows返回值不同的原因是啥,如何解决?
Posted
技术标签:
【中文标题】Linux和Windows返回值不同的原因是啥,如何解决?【英文标题】:What is the cause of the difference in return value between Linux and Windows and how to fix it?Linux和Windows返回值不同的原因是什么,如何解决? 【发布时间】:2016-07-06 15:58:15 【问题描述】:这是我试图返回一个类的对象的代码。但是我从 CentOs (gcc) 和 Visual Studio 2013 得到了不同的结果。在 cls.cpp 中,使用 gcc,它运行良好,我得到了诸如检测器名称 =“t_name”,检测器步幅 = 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;
【问题讨论】:
“不同的答案”是什么意思?不同的如何? @nvoigt 与 gcc,tt.name = "t_name"
和 tt.stride = 5
,但我在 Visual Studio 中得到了 tt.name = ""
和 tt.stride = 0
。
【参考方案1】:
你有一个复制构造函数,什么都不做。
cls::cls(const cls& origin)
GCC 可能会在 cls tt = func();
和 cls detector = builder->build();
行中执行 copy elision 并且它神奇地起作用。启用优化后,也许 VS 也会这样做。就个人而言,我会说这是您代码中的错误。如果您有复制构造函数,则复制原始对象。
【讨论】:
更好的是,根本没有复制构造函数! 它不仅是一个错误,而且它现在是自 RVO 以来的 UB,不必应用复制省略(C++17 将改变这一点)。 是的,删除 cls::cls(const cls& origin) 或 cls::cls(const cls& origin)=default 都可以解决这个问题。【参考方案2】:我使用 Visual Studio 2015 尝试了您的代码,发现析构函数 ~cls() 执行了两次。对我来说这似乎是一个错误,但我不是专家。
“deconstructed”是指“destructed”吗?
我在 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() 的堆栈帧中构造并破坏了对象,将对象复制到 main() 的堆栈帧,将“tt”上的析构函数调用为 func( ) 退出,并在 main() 退出时在第二个实例上再次调用析构函数。别介意赋值运算符是私有的,而复制构造函数是一个空函数。
很遗憾,我不是专家,无法说出这与标准相比如何。
你说使用一个编译器,“检测器”的值是“t_name”和 5,但使用 Visual Studio,值是“”和 0。(你怎么知道?代码不输出值,所以我猜你您的代码中可能有更多的 OUT...)
我的猜测是编译器使用了空的复制构造函数(cls.cpp 第 3 行),因此 main() 的堆栈帧(“检测器”)中的副本没有复制到其中的数据。教训也许是,你不能仅仅通过否认它愚蠢地表现来强迫 Visual Studio 表现得聪明。如果 VS 没有能力在 main() 的“detector”变量中构造 func() 的“tt”变量,那么当你锁定赋值运算符时,它不会不情愿地获得这个能力。
也许我们都应该仔细了解何时使用赋值运算符,以及何时使用复制构造函数。似乎 Visual Studio 解释了您的代码
cls tt = func();
好像是
cls tt( func() );
使用复制构造函数初始化tt。或许只有当目标是一个已经正确初始化的对象时才使用赋值运算符。
【讨论】:
非常感谢!我在调试时得到了值。你的分析真的很有帮助:)以上是关于Linux和Windows返回值不同的原因是啥,如何解决?的主要内容,如果未能解决你的问题,请参考以下文章
当为“var”和“let”分配一个引发错误的函数的返回值时,是啥导致了它们之间的不同行为