返回对象时编译器做什么

时间:2014-07-25 02:58:01

标签: c++

我有以下类叫做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;
};

我有以下测试:

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

运行测试后,输出为:

  

在GetData调用之前

     

获取数据

     

在构造函数

中      

退出GetData

     

5

     

获取数据通话后

     

退出主要

     

在析构函数

我有以下问题:

  1. 当我调用DATA dat=GetData();时,它既不调用构造函数,也不调用构造函数,也不调用运算符。如何构造dat对象。从GetData返回时,编译器究竟做了什么?

  2. 对于DATA结构或一般聚合数据类型,使用new初始化成员变量总是一个好主意吗?初始化时,成员变量会发生什么变化DATA *d=new DATA(DATATYPE_NUMBER)?内存泄漏是否更容易出错?

3 个答案:

答案 0 :(得分:1)

问题1

当我调用DATA dat=GetData();时,它既不调用构造函数,也不调用构造函数,也不调用运算符。如何构造dat对象。从GetData返回时,编译器究竟做了什么?

回答这是返回值优化(RVO)的结果。您可以阅读有关他们的更多信息here

在g ++中,您可以使用标记-fno-elide-constructors禁用RVO。如果您使用代码执行此操作,您将看到来自复制构造函数的消息。

问题2

对于DATA结构,或者通常的聚合数据类型,使用new初始化成员变量总是一个好主意吗?初始化时,成员变量会发生什么变化DATA *d=new DATA(DATATYPE_NUMBER)?内存泄漏是否容易出错?

<强>答案

有三个问题。

回答2.1

该问题的答案是&#34;取决于您的应用程序&#34;。对于某些人来说,将对象作为成员数据是有意义的,而对于其他人来说,指向对象是有意义的。当您使用指向对象的指针时,您必须遵循已成为Rule of ThreeRule of Five in C++11

回答2.2

成员变量的初始化与使用时一样:

Data d = DATA(DATATYPE_NUMBER);

回答2.3

使用动态内存有好处,但它也有其缺点。无论何时使用动态内存分配,您都会遇到更容易出错的代码。你必须担心其潜在的不良副作用:

  1. 悬空指针。
  2. 迷失指针。
  3. 访问超出分配内存的内存。

答案 1 :(得分:1)

  

当我调用DATA dat = GetData()时;它既不调用构造函数,也不复制构造函数,也不调用运算符如何构造dat对象。从GetData返回时编译器究竟做了什么?

等于运算符:即使它看起来像你正在分配,但事实并非如此。语法T t = u;不包含任何赋值,而是复制构造。

构造函数:在函数内部调用构造函数,然后返回 copy copy 依次复制< / em>在目标对象上。除非它不是。该语言允许 copy-elision ,这意味着允许编译器通过将三个对象(ret放置在函数内,返回的对象和dat在main内部来删除副本)在同一个记忆位置。

我不确定确切的细节有多重要或有多大帮助,但在大多数ABI中(据我所知),一个按值返回的函数,一个对象被编译器转换为一个函数,该函数获取指向所在位置的指针对象将会存在。

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

转化为:

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

字面意思是copy-elision函数,f在返回值(参数)之上创建了tmp对象,main中的调用者放置了tf在同一内存位置的返回值。

  

对于DATA结构,或者一般的聚合数据类型,使用new初始化成员变量总是一个好主意吗?初始化时成员变量会发生什么,说DATA * d = new DATA(DATATYPE_NUMBER)?内存泄漏是否容易出错?

你告诉我。但在回答之前,请注意您的DATA类型有内存泄漏。

答案 2 :(得分:0)

使构造函数显式化并删除DATA(enum,int,int)上的默认参数。当GetData()使用单个参数构造对象时,您最有可能获得编译器生成的默认构造函数。

你可以通过输入m_str&amp;来节省内存。联合中的m_number而不是单独的成员变量:

DATA {
...

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

当成员很大或者为了提高性能而构建起来时,使用成员指针既美观又常见。但你必须采取额外的措施,以避免内存泄漏。 OTOH,如果成员很小,常规成员变量的便利性通常是最好的。