简体   繁体   中英

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. It seems in vs2013 the cls object is deconstructed. Why I got the different returns? and how to make it works well under visual studio? Thanks a lot.

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

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(); and cls detector = builder->build(); and it magically works. Maybe VS will do it too once you enable optimizations. 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. 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. 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.

Now i am trying again... In Visual Studio, I am setting a breakpoint at the line reading OUT("~cls()"); (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. 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...)

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. The lesson is, perhaps, that you cannot force Visual Studio to behave cleverly just by denying it to behave stupidly. 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.

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

cls tt = func();

as if it were

cls tt( func() );

using the copy constructor to initialize tt. Perhaps the assignment operator is used only when the destination is an object that has already been properly initialized.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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