[英]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: 我有以下问题:
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
返回时,编译器还会执行什么操作?
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:
您必须担心其潜在的不良副作用:
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
的调用者将t
和f
的返回值放在相同的内存位置上。
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.