简体   繁体   English

返回对象时编译器会做什么

[英]What does compiler do when returning an object

I have the following class called DATA. 我有一个名为DATA的以下课程。

enum DATATYPE{DATATYPE_CONSTANT=0, DATATYPE_NUMBER,DATATYPE_STRING, DATATYPE_MATRIX, DATATYPE_OBJECT};

struct DATA //A Data container for the variable
{
    DATA(DATATYPE type,int row=0,int col=0)
    {
        m_str=0;m_number=0;

        m_DataType=type;

        if(type==DATATYPE_NUMBER) m_number=new double;
        if(type==DATATYPE_STRING) m_str=new string("");

        cout<<"In constructor"<<endl;
        //if(type==DATATYPE_MATRIX) m_matrix= new MatrixXd(row,col);
    }
    ~DATA()
    {
        if(m_str) m_str->clear();
        if(m_number) {delete m_number; m_number=0;}

        std::cout<<"In Destructor"<<std::endl;
        //if(m_matrix) {delete m_matrix; m_matrix=0;}
    }

    DATA(const DATA& other)
    {
        m_number=other.m_number;
        m_str=other.m_str;
        m_DataType=other.m_DataType;

        cout<<"In copy constructor"<<endl;
    }

    DATA& operator=(const DATA& other)
    {
        m_number=other.m_number;
        m_str=other.m_str;
        m_DataType=other.m_DataType;

        cout<<"In operator="<<endl;

        return *this;
    }

    DATATYPE GetType()
    {
        return m_DataType;
    }

    double* GetNumber()
    {
        return m_number;
    }

    void SetNumber(const double& val){*m_number=val;}


    string* GetString()
    {
        return m_str;
    }

private:
    DATATYPE m_DataType;
    string* m_str;
     //MatrixXd* m_matrix;
    double* m_number;
};

And I have the following test: 我有以下测试:

DATA GetData();

int main()
{
    cout<<"Before GetData call"<<endl;
    DATA dat=GetData();
    //DATA dat2=dat;
    cout<<*(dat.GetNumber())<<endl;
    cout<<"After Get Data call"<<endl;

    cout << "Exiting main" << endl;
    return 0;
}


DATA GetData()
{
    cout<<"In Get Data"<<endl;
    DATA ret(DATATYPE_NUMBER);
    double d=5;
    ret.SetNumber(d);
    cout<<"Exiting GetData"<<endl;
    return ret;
}

After running the test the output is: 运行测试后,输出为:

Before GetData call 调用GetData之前

In Get Data 在获取数据中

In constructor 在构造函数中

Exiting GetData 退出GetData

5 5

After Get Data call 在获取数据后

Exiting main 退出主要

In Destructor 在析构函数中

I have the following questions: 我有以下问题:

  1. When I call DATA dat=GetData(); 当我调用DATA dat=GetData(); it neither calls constructor, copy constructor nor equal operator. 它既不调用构造函数,复制构造函数,也不调用相等的运算符。 How is dat object constructed. dat对象如何构造。 Also what exactly compiler do when returning from GetData ? GetData返回时,编译器还会执行什么操作?

  2. For the DATA structure, or in general aggregate data types, is it always a good idea to initialize member variables with new ? 对于DATA结构或一般的聚合数据类型,用new初始化成员变量总是一个好主意吗? What happens to member variables when I initialize say DATA *d=new DATA(DATATYPE_NUMBER) ? 当我初始化说DATA *d=new DATA(DATATYPE_NUMBER)时,成员变量会发生什么? Is it getting more error prone for memory leaks? 内存泄漏是否容易出错?

Question 1 问题1

When I call DATA dat=GetData(); 当我调用DATA dat=GetData(); it neither calls constructor, copy constructor nor equal operator. 它既不调用构造函数,复制构造函数,也不调用相等的运算符。 How is dat object constructed. dat对象如何构造。 Also what exactly compiler do when returning from GetData ? GetData返回时,编译器还会执行什么操作?

Answer That is a result of something called return value optimization (RVO) . 答案这是所谓的返回值优化(RVO)的结果 You can read more about them here . 您可以在此处阅读有关它们的更多信息。

In g++, you can disable RVO by using the flag -fno-elide-constructors . 在g ++中,可以使用标志-fno-elide-constructors禁用RVO。 If you do that with your code, you will see messages from the copy constructor. 如果使用代码执行此操作,则将看到来自复制构造函数的消息。

Question 2 问题2

For the DATA structure, or in general aggregate data types, is it always a good idea to initialize member variables with new? 对于DATA结构或一般的聚合数据类型,用new初始化成员变量总是一个好主意吗? What happens to member variables when I initialize say DATA *d=new DATA(DATATYPE_NUMBER) ? 当我初始化说DATA *d=new DATA(DATATYPE_NUMBER)时,成员变量会发生什么? Is it getting more error prone for memory leaks? 内存泄漏是否容易出错?

Answer 回答

There are three question in that. 有三个问题。

Answer 2.1 答案2.1

The answer to that question is "depends on your application". 该问题的答案是“取决于您的应用程序”。 For some, having objects as member data makes sense while for others, having pointers to objects make sense. 对于某些人而言,将对象用作成员数据是有意义的,而对于另一些人而言,将指向对象的指针有意义。 When you use pointers to objects, you have to follow the Rule of Three , which has become the Rule of Five in C++11 . 当使用指向对象的指针时,必须遵循“三规则” ,这已成为C ++ 11中的“五规则”

Answer 2.2 答案2.2

The member variables are initialized just as they would be had you used: 成员变量被初始化,就像您使用它们时那样:

Data d = DATA(DATATYPE_NUMBER);

Answer 2.3 答案2.3

Using dynamic memory has benefits, but it also has its down sides. 使用动态内存有好处,但也有缺点。 Any time you use dynamic memory allocation, you are entering into more error prone code. 每当使用动态内存分配时,您都会输入更多易于出错的代码。 You have to worry about its potential bad side effects: 您必须担心其潜在的不良副作用:

  1. Dangling pointers. 悬空的指针。
  2. Lost pointers. 丢失指针。
  3. Accessing memory beyond what was allocated. 访问超出分配范围的内存。

When I call DATA dat=GetData(); 当我调用DATA dat = GetData(); it neither calls constructor, copy constructor nor equal operator. 它既不调用构造函数,复制构造函数,也不调用相等的运算符。 How is dat object constructed. dat对象如何构造。 Also what exactly compiler do when returning from GetData? 从GetData返回时,编译器还会执行什么操作?

equal operator : Even if it may look like you are assigning, that is not the case. 等于运算符 :即使看起来像您正在分配,也不是这种情况。 The syntax T t = u; 语法T t = u; does not contain any assignment, but rather copy construction. 不包含任何分配,而是复制构造。

constructor : The constructor is called inside the function, then a copy is returned and that copy is in turn copied over the destination object. 构造器 :构造函数调用的函数里面,然后返回一个副本 ,该副本是又复制了目标对象。 Except it isn't. 除非不是。 The language allows for copy-elision which means that the compiler is allowed to remove the copies by placing the three objects ( ret inside the function, returned object and dat inside main) in the same memory location. 该语言允许复制删除,这意味着允许编译器通过将三个对象(函数内部的ret ,返回的对象和main的dat )放置在相同的存储位置中来删除副本。

I am not sure how much the exact details matter or will help, but in most ABIs (in all I know) a function returning by value an object is transformed by the compiler into a function that takes a pointer to the location where the object will live. 我不确定确切的细节有多大作用或将有多少帮助,但是在大多数ABI(据我所知)中,按值返回对象的函数由编译器转换为一个函数,该函数采用指向对象将要到达的位置的指针生活。

T f(int x) {
   T tmp(x);
   return tmp;
}
int main() {
   T t = f(1);
}

Is transformed into: 转换为:

void f(void *__ret, int x) {
   new (__ret) T(x); // constructor call
   return;                               // return does not *copy*
}
int main() {
   [[uninitialized]] T t;                // space is reserved, no construction
   f(&t, 1);
}

That is literally how copy-elision functions, f created the tmp object on top of the returned value (the argument) and the caller in main placed the t and the return value of f over the same memory location. 从字面上看,这就是复制删除功能的工作方式, f在返回值(参数)的顶部创建了tmp对象,并且main的调用者将tf的返回值放在相同的内存位置上。

For the DATA structure, or in general aggregate data types, is it always a good idea to initialize member variables with new? 对于DATA结构或一般的聚合数据类型,用new初始化成员变量总是一个好主意吗? What happens to member variables when I initialize say DATA *d=new DATA(DATATYPE_NUMBER) ? 当我初始化说DATA * d = new DATA(DATATYPE_NUMBER)时,成员变量会发生什么? Is it getting more error prone for memory leaks? 内存泄漏是否容易出错?

You tell me. 你告诉我。 But before you answer, note that your DATA type has a memory leak. 但是在回答之前,请注意您的DATA类型有内存泄漏。

Make your constructors explicit and remove the default parameters on DATA(enum,int,int). 使构造函数明确,并删除DATA(enum,int,int)上的默认参数。 When GetData() constructs the object with a single parameter you are most likely getting a compiler generated default constructor. 当GetData()使用单个参数构造对象时,您很可能会获得编译器生成的默认构造函数。

You can save memory by putting m_str & m_number in a union rather than separate member variables: 您可以通过将m_str和m_number放入联合而不是单独的成员变量来节省内存:

DATA {
...

union U {
   string* m_string;
   double* m_number;
}
    DATATYPE m_DataType;
    U m_data;
}

Using pointers for members is both fine and common when the members are large or can be constructed late for performance. 当成员很大或可以为提高性能而延迟构造时,对成员使用指针既好又常见。 But you do have to take additional care to avoid memory leaks. 但是您必须格外小心,以避免内存泄漏。 OTOH, if the members are small, the convenience of a regular member variable is frequently best. OTOH,如果成员较小,则通常最好使用常规成员变量的便利性。

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

相关问题 新建对象并将对象的地址分配给其基类指针时,编译器会做什么? - what does compiler do when new an object and assign the address of the created object to its base class pointer C ++编译器如何创建对象? - What does a C++ compiler do to create an object? 编译器遇到return语句怎么办? - What does the compiler do when encountering a return statement? 编译器将浮点变量转换为整数变量时会做什么? - What does the compiler do when it converts a float variable to an integer variable? 返回对本地对象的引用时的编译器警告 - Compiler warning when returning a reference to a local object 当我包括一个类如 <vector> 并且只使用其成员函数中的1个或2个,编译器会做什么? - When I include a class such as <vector> and only use 1 or 2 of its member functions, what does the compiler do? 当出现模糊的默认参数时,C ++编译器会做什么? - What does the C++ compiler do when coming ambiguous default parameters? 优化代码时编译器在汇编中做了什么?即-O2标志 - What does the compiler do in assembly when optimizing code? ie -O2 flag 在返回作为const引用的字符串时,编译器是否避免使用副本? - Does the compiler avoid a copy when returning a string that is taken as const reference? 编译器用a [i]做什么?a是数组? 如果a是指针怎么办? - what does compiler do with a[i] which a is array? And what if a is a pointer?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM