【C/C++】【类和对象】临时对象
临时对象
C++中真正的临时对象是不可见的,在源代码中不会出现,且不在堆上分配内存(在栈中),没有名字的对象;
//i++ ++i
//i++ 会产生一个临时对象,用来记录i的值;
int i = 1;
int &&r = i++; //i和r没关系
产生临时对象的情况
临时对象可能发生于如下的三种情况,我们需要了解这些临时对象如何被产生和被销毁;以及如何避免产生临时对象,避免不必要的调用构造和析构;进一步提升程序的性能;
- 传值方式给函数传参
- 隐式类型转换
- 函数返回对象
传值方式给函数传参
- 按照值传递的方式给函数传参,函数会产生一个实参的副本,在函数中所有的操作都是针对这个副本的,也正是因为这个原因,对副本的修改并不会影响实参的值;
- 通过采用引用传值的方式,不用创建实参的副本,少调用一次拷贝构造和一次析构函数,同时可以通过形参来修改实参的值;
#include <iostream>
using namespace std;
class CTempValue
{
public:
int m_val;
int m_v;
public:
CTempValue(int i = 0, int j = 0);
CTempValue(const CTempValue& t) :m_val(t.m_val), m_v(t.m_v)
{
cout << "执行拷贝构造函数" << endl;
}
virtual ~CTempValue()
{
cout << "执行析构函数" << endl;
}
int add(CTempValue tmp); //普通函数
};
CTempValue::CTempValue(int i, int j):m_val(i), m_v(j)
{
cout << "执行构造函数" << endl;
cout << "m_val = " << m_val << endl;
cout << "m_v = " << m_v << endl;
}
int CTempValue::add(CTempValue tmp)
{
int tmp_val = tmp.m_val + tmp.m_v;
tmp.m_val = 1000;
return tmp_val;
}
int main()
{
CTempValue tmp(10, 20); //构造函数执行
int Sum = tmp.add(tmp); //拷贝构造函数和析构函数会执行
//add的形式参数会拷贝实参 使用拷贝构造函数
//形参的释放 调用析构函数
cout << "Sum = " << Sum << endl;
cout << "tmp.m_val = " << tmp.m_val << endl;
}
通过引用传参解决
#include <iostream>
using namespace std;
class CTempValue
{
public:
int m_val;
int m_v;
public:
CTempValue(int i = 0, int j = 0);
CTempValue(const CTempValue& t) :m_val(t.m_val), m_v(t.m_v)
{
cout << "执行拷贝构造函数" << endl;
}
virtual ~CTempValue()
{
cout << "执行析构函数" << endl;
}
int add(CTempValue& tmp); //普通函数
};
CTempValue::CTempValue(int i, int j):m_val(i), m_v(j)
{
cout << "执行构造函数" << endl;
cout << "m_val = " << m_val << endl;
cout << "m_v = " << m_v << endl;
}
int CTempValue::add(CTempValue& tmp)
{
int tmp_val = tmp.m_val + tmp.m_v;
tmp.m_val = 1000;
return tmp_val;
}
int main()
{
CTempValue tmp(10, 20); //构造函数执行
int Sum = tmp.add(tmp); //拷贝构造函数和析构函数不会执行
cout << "Sum = " << Sum << endl;
cout << "tmp.m_val = " << tmp.m_val << endl;
}
类型转换
- 将值赋给某个变量的时候(传递对象给一个参数),其类型和它即将绑定上去的参数类型不同会发生隐式类型转化;
示例1
将一个整型值赋值给一个CTempValue对象,会发生隐式类型转换;
- 会产生一个临时对象;
- 会调用拷贝赋值运算符把这个临时对象里面的各个成员赋值给sum对象
- 销毁这个临时创建的CTempValue对象
#include <iostream>
using namespace std;
class CTempValue
{
public:
int m_val;
int m_v;
public:
CTempValue(int i = 0, int j = 0);
CTempValue(const CTempValue& t) :m_val(t.m_val), m_v(t.m_v)
{
cout << "执行拷贝构造函数" << endl;
}
//拷贝赋值运算符
CTempValue& operator=(const CTempValue& tmp)
{
m_val = tmp.m_val;
m_v = tmp.m_v;
cout << "执行拷贝赋值运算符" << endl;
return *this;
}
virtual ~CTempValue()
{
cout << "执行析构函数" << endl;
}
int add(CTempValue& tmp); //普通函数
};
CTempValue::CTempValue(int i, int j):m_val(i), m_v(j)
{
cout << "执行构造函数" << endl;
cout << "m_val = " << m_val << endl;
cout << "m_v = " << m_v << endl;
}
int CTempValue::add(CTempValue& tmp)
{
int tmp_val = tmp.m_val + tmp.m_v;
tmp.m_val = 1000;
return tmp_val;
}
int main()
{
CTempValue sum;
sum = 1000; //会产生一个临时对象; 调用一次构造函数一次析构函数
}
解决方法
-
通过定义时初始化对象来达到不生成临时对象的目的;
-
=是定义时初始化;系统为对象预留空间 用1000构造对象,而且是直接构造在对象预留空间中;
#include <iostream> using namespace std; class CTempValue { public: int m_val; int m_v; public: CTempValue(int i = 0, int j = 0); CTempValue(const CTempValue& t) :m_val(t.m_val), m_v(t.m_v) { cout << "执行拷贝构造函数" << endl; } //拷贝赋值运算符 CTempValue& operator=(const CTempValue& tmp) { m_val = tmp.m_val; m_v = tmp.m_v; cout << "执行拷贝赋值运算符" << endl; return *this; } virtual ~CTempValue() { cout << "执行析构函数" << endl; } int add(CTempValue& tmp); //普通函数 }; CTempValue::CTempValue(int i, int j):m_val(i), m_v(j) { cout << "执行构造函数" << endl; cout << "m_val = " << m_val << endl; cout << "m_v = " << m_v << endl; } int CTempValue::add(CTempValue& tmp) { int tmp_val = tmp.m_val + tmp.m_v; tmp.m_val = 1000; return tmp_val; } int main() { /* CTempValue sum; sum = 1000; */ //会产生一个临时对象; 调用一次构造函数一次析构函数 一次拷贝赋值运算符 //1. 用1000创建CTempValue的临时对象 //2. 调用拷贝赋值运算符把这个临时对象里面的各个成员赋值给sum对象 //3. 销毁这个临时创建的CTempValue对象 CTempValue sum = 1000; //没有生成临时对象 =是定义时初始化;系统为sum预留空间 用1000构造sum对象,而且是直接构造在sum对象预留空间中; }
示例2
#include <iostream>
#include <string.h>
#include <vector>
using namespace std;
class MyString
{
private:
char* m_data;
int m_size;
public:
//确实存在隐式类型转换,如果加上explicit代码报错
//explicit MyString(const char* str = nullptr)
MyString(const char* str = nullptr)
{
cout << "MyString(const char* str = nullptr)" << endl;
if(str == nullptr)
{
m_data = new char[1];
m_data[0] = '\0';
m_size = 0;
}
else
{
m_size = strlen(str);
m_data = new char[m_size + 1];
strcpy(m_data, str);
}
}
~MyString()
{
cout << "~MyString()" << endl;
delete[] m_data;
}
MyString(const MyString& str)
{
cout << "MyString(const MyString& str)" << endl;
m_size = str.m_size;
m_data = new char[m_size + 1];
strcpy(m_data, str.m_data);
}
MyString& operator=(const MyString& str)
{
cout << "MyString& operator=(const MyString& str)" << endl;
if(this == &str) return *this;
delete[] m_data;
m_size = str.m_size;
m_data = new char[m_size + 1];
strcpy(m_data, str.m_data);
return *this;
}
MyString(MyString &&str)
{
cout << "MyString(MyString &&str)" << endl;
m_size = str.m_size;
m_data = str.m_data;
str.m_data = nullptr;
}
MyString& operator=(MyString &&str)
{
if(this == &str) return *this;
cout << "MyString& operator=(MyString &&str)" << endl;
delete[] m_data;
m_size = str.m_size;
m_data = str.m_data;
str.m_data = nullptr;
return *this;
}
char* get_data() const
{
return m_data;
}
friend ostream&operator<< (ostream& os, const MyString& str);
friend istream&operator>> (istream& is, MyString& str);
MyString operator+(const MyString& str)
{
int len = m_size + str.m_size;
MyString res;
delete[] res.m_data;
res.m_size = len;
res.m_data = new char[len + 1];
memset(res.m_data, 0, len + 1);
strcat(res.m_data, m_data);
strcat(res.m_data, str.m_data);
return res;
}
};
ostream&operator<< (ostream& os, const MyString& str)
{
os << str.m_data;
return os;
}
istream&operator>> (istream& is, MyString& str)
{
delete[] str.m_data;
char buf[1024];
scanf("%s", buf);
int len = strlen(buf);
str.m_data = new char[len + 1];
strcpy(str.m_data, buf);
return is;
}
MyString get_Str(MyString& str)
{
return MyString(str.get_data());
}
//统计字符ch在str中出现的次数
//将str绑定到string临时对象上 去掉const报错 系统不允许修改临时对象
int total(const MyString& str, char ch)
{
const char* p = str.get_data();
int cnt = 0;
return cnt;
}
int main()
{
char mystr[100] = "hello world";
int res = total(mystr, 'o');//隐式类型转化 会产生临时对象MyString 确实存在隐式类型转换,如果加上explicit代码报错
//c++只会为const引用产生临时对象,而不会为非const引用产生临时对象???
cout << res << endl;
return 0;
}
优化
函数返回对象
当函数需要返回一个对象,它会在栈中创建一个临时对象,存储函数的返回值。
范例1
#include <iostream>
using namespace std;
class CTempValue
{
public:
int m_val;
int m_v;
public:
CTempValue(int i = 0, int j = 0);
CTempValue(const CTempValue& t) :m_val(t.m_val), m_v(t.m_v)
{
cout << "执行拷贝构造函数" << endl;
}
//拷贝赋值运算符
CTempValue& operator=(const CTempValue& tmp)
{
m_val = tmp.m_val;
m_v = tmp.m_v;
cout << "执行拷贝赋值运算符" << endl;
return *this;
}
virtual ~CTempValue()
{
cout << "执行析构函数" << endl;
}
int add(CTempValue& tmp); //普通函数
};
CTempValue::CTempValue(int i, int j):m_val(i), m_v(j)
{
cout << "执行构造函数" << endl;
cout << "m_val = " << m_val << endl;
cout << "m_v = " << m_v << endl;
}
int CTempValue::add(CTempValue& tmp)
{
int tmp_val = tmp.m_val + tmp.m_v;
tmp.m_val = 1000;
return tmp_val;
}
CTempValue Double(CTempValue& tmp)
{
CTempValue tmp_value; //构造 + 析构
tmp_value.m_v = tmp.m_v * 2;
tmp_value.m_val = tmp.m_val * 2;
return tmp_value; //产生临时对象 调用拷贝构造函数和析构函数
}
int main()
{
CTempValue x(10, 20); //构造 + 析构
//Double(x);
CTempValue&& y = Double(x); //临时对象是一种右值
CTempValue z = Double(x);
}
优化
#include <iostream>
using namespace std;
class CTempValue
{
public:
int m_val;
int m_v;
public:
CTempValue(int i = 0, int j = 0);
CTempValue(const CTempValue& t) :m_val(t.m_val), m_v(t.m_v)
{
cout << "执行拷贝构造函数" << endl;
}
//拷贝赋值运算符
CTempValue& operator=(const CTempValue& tmp)
{
m_val = tmp.m_val;
m_v = tmp.m_v;
cout << "执行拷贝赋值运算符" << endl;
return *this;
}
virtual ~CTempValue()
{
cout << "执行析构函数" << endl;
}
int add(CTempValue& tmp); //普通函数
};
CTempValue::CTempValue(int i, int j):m_val(i), m_v(j)
{
cout << "执行构造函数" << endl;
cout << "m_val = " << m_val << endl;
cout << "m_v = " << m_v << endl;
}
int CTempValue::add(CTempValue& tmp)
{
int tmp_val = tmp.m_val + tmp.m_v;
tmp.m_val = 1000;
return tmp_val;
}
CTempValue Double(CTempValue& tmp)
{
return CTempValue(tmp.m_val * 2, tmp.m_v * 2);
}
int main()
{
CTempValue x(10, 20); //构造 + 析构
//Double(x);
CTempValue&& y = Double(x); //临时对象是一种右值
//CTempValue z = Double(x);
}
范例2:类外运算符重载
class mynum
{
public:
int x;
int y;
};
mynum operator+(mynum& num1, mynum& num2)
{
mynum res;
res.x = num1.x + num2.x;
res.y = num1.y + num2.y;
return res;
}
int main()
{
mynum num1;
num1.x = 1;
num1.y = 2;
mynum num2;
num2.x = 3;
num2.y = 4;
mynum num3 = num1 + num2;
}
优化
class mynum
{
public:
int x;
int y;
public:
mynum(int x = 0, int y = 0) :x(x), y(y) {};
};
mynum operator+(mynum& num1, mynum& num2)
{
return mynum(num1.x + num2.x, num1.y + num2.y);
}
int main()
{
mynum num1;
num1.x = 1;
num1.y = 2;
mynum num2;
num2.x = 3;
num2.y = 4;
mynum num3 = num1 + num2;
}
总结
为了避免产生临时对象,提高效率:
- 函数调用传递对象时,函数形参应该按引用来接收。
- 函数返回对象时,应该直接返回临时对象,不要先定义再返回
- 调用返回对象的函数时,应该以初始化的形式调用(定义的同时初始化),而不是先定义对象再赋值。
除此之外还可以使用内联函数来优化函数调用;
参考:《More Effective C++》条款19(p98)
知识的价值不在于占有,而在于使用

浙公网安备 33010602011771号