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-&gt;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返回值不同的原因是啥,如何解决?的主要内容,如果未能解决你的问题,请参考以下文章

java中重载和重写的区别是啥?

当为“var”和“let”分配一个引发错误的函数的返回值时,是啥导致了它们之间的不同行为

用secureCRT链接远程linux操作系统,出现“远程主机拒绝链接”的提示是啥原因?

我的电脑反应慢 是啥原因?

C语言中函数和函数体的区别是啥?

Cmake是啥?有啥用?在WINDOWS下如何用