简体   繁体   English

Linux和Windows之间的返回值不同的原因是什么?如何解决?

[英]What is the cause of the difference in return value between Linux and Windows and how to fix it?

Here is the code that I tried to return an object of a class. 这是我尝试返回类对象的代码。 but I got different results from CentOs (gcc) and visual studio 2013. In the cls.cpp, with gcc, it works well, I get the results such as detector.name = "t_name", detector.stride = 5. but the values in the detector are "" and 0 under vs2013. 但是我从CentOs(gcc)和Visual Studio 2013得到了不同的结果。在使用gcc的cls.cpp中,它运行良好,得到的结果是detector.name =“ t_name”,detector.stride = 5,但是vs2013下检测器中的值为“”和0。 It seems in vs2013 the cls object is deconstructed. 在vs2013中,似乎对cls对象进行了解构。 Why I got the different returns? 为什么我得到不同的回报? and how to make it works well under visual studio? 以及如何使其在Visual Studio下正常工作? Thanks a lot. 非常感谢。

cls.h 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 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 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;
}

You have got a copy constructor that does nothing . 您有一个不执行任何操作的复制构造函数。

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

GCC probably does copy elision in the lines cls tt = func(); GCC可能确实在cls tt = func();复制了省略号 cls tt = func(); and cls detector = builder->build(); cls detector = builder->build(); and it magically works. 它神奇地工作。 Maybe VS will do it too once you enable optimizations. 启用优化后,也许VS也可以做到。 Personally, I'd say it's a bug in your code. 就个人而言,我会说这是您代码中的错误。 If you have a copy constructor, then copy the origin object. 如果您有复制构造函数,则复制原始对象。

I tried your code using Visual Studio 2015, and found that the destructor ~cls() executes twice. 我使用Visual Studio 2015尝试了您的代码,发现析构函数〜cls()执行两次。 It looks like a bug to me, but I am not an expert. 对我来说,这似乎是个错误,但我不是专家。

By "deconstructed", do you mean "destructed"? “破坏”是指“破坏”吗?

I compiled compiled the code using Gnu g++ under Windows 10 64-bit, under cygwin. 我在cygwin下的Windows 10 64位下使用Gnu g ++编译了代码。 This time the destructor ran only once. 这次,析构函数只运行了一次。

I seem to remember that when a function constructs an object and returns it (like your "cls tt = func();", the compiler may chose to construct the object in the final location, that is, on the stack frame of the calling function. I don't know if this behavior is mandatory. 我似乎记得,当一个函数构造一个对象并返回它时(例如您的“ cls tt = func();”),编译器可能会选择在最终位置即调用的堆栈框架上构造该对象。我不知道这种行为是否是强制性的。

Now i am trying again... In Visual Studio, I am setting a breakpoint at the line reading OUT("~cls()"); 现在,我再次尝试...在Visual Studio中,我在读取OUT(“〜cls()”);的行上设置断点; (cls.cpp:12). (cls.cpp:12)。 The first time the breakpoint is hit, the stack trace is: 第一次遇到断点时,堆栈跟踪为:

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

Clicking "continue" in the debugger, hitting the breakpoint again. 在调试器中单击“继续”,再次达到断点。 Now the stack is: 现在堆栈是:

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

So it seems that Visual Studio does construct -- and destruct -- the object in func()'s stack frame, copy the object to main()'s stack frame, call the destructor on "tt" as func() exits, and call the destructor again on the second instance when main() exits. 因此,似乎Visual Studio确实在func()的堆栈框架中构造了对象并对其进行了销毁,将对象复制到main()的堆栈框架中,在func()退出时在“ tt”上调用析构函数,并在main()退出时在第二个实例上再次调用析构函数。 Never mind that the assignment operator is private, and the copy constructor is an empty function. 没关系,赋值运算符是私有的,而复制构造函数是一个空函数。

Unfortunately, I am no expert and cannot say how this compares to the standard(s). 不幸的是,我不是专家,不能说这与标准相比。

You say that using one compiler, "detector" has values "t_name" and 5, but using Visual Studio the values are "" and 0. (How do you know? The code does not output values, so I guess you may have had more OUT's in your code...) 您说使用一个编译器,“检测器”的值是“ t_name”和5,但是使用Visual Studio时,值是“”和0。(您怎么知道?代码不会输出值,所以我想您可能已经您的代码中还有更多OUT ...)

My guess is that the compiler used the empty copy constructor (cls.cpp line 3), and so the copy in main()'s stack frame ("detector") had no data copied into it. 我的猜测是,编译器使用了空副本构造函数(cls.cpp第3行),因此main()的堆栈帧中的副本(“检测器”)没有数据复制到其中。 The lesson is, perhaps, that you cannot force Visual Studio to behave cleverly just by denying it to behave stupidly. 可能的教训是,您不能仅仅通过拒绝Visual Studio愚蠢的行为来强迫Visual Studio明智地运行。 If VS does not have the ability to construct func()'s "tt" variable inside main()'s "detector" variable, it does not reluctantly acquire this ability when you lock down the assignment operator. 如果VS无法在main()的“ detector”变量中构造func()的“ tt”变量,那么当您锁定赋值运算符时,它不会不情愿地获得此功能。

Perhaps we both should read up on when exactly the assignment operator is used, and when the copy constructor is used. 也许我们俩都应该仔细阅读何时确切使用赋值运算符以及何时使用复制构造函数。 It seems like Visual studio interpreted your code 好像Visual Studio解释了您的代码

cls tt = func();

as if it were 好像

cls tt( func() );

using the copy constructor to initialize tt. 使用复制构造函数初始化tt。 Perhaps the assignment operator is used only when the destination is an object that has already been properly initialized. 也许仅当目标是已经正确初始化的对象时才使用赋值运算符。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM