1. 对象使用过程中背后调用了哪些方法
#include<iostream>
using namespace std;
class Test
{
public:
Test(int a = 10) :ma(a)
{
cout << "Test(int)" << endl;
}
~Test()
{
cout << "~Test()" << endl;
}
Test(const Test& t) :ma(t.ma)
{
cout << "Test(const Test& t)" << endl;
}
Test& operator=(const Test& t)
{
cout << "operator=" << endl;
ma = t.ma;
return *this;
}
private:
int ma;
};
int main()
{
Test t1;
Test t2(t1);
Test t3 = t1;
//Test(20)显示生成临时对象 生存周期:所在语句
/*
C++编译器对于对象构造的优化:用临时对象生成新对象的时候,临时对象就不产生了,直接构造新对象就可以了
*/
Test t4 = Test(20);//Test t4(20);没有区别
cout << "--------------------" << endl;
t4 = t2;
//显式生成临时对象
t4 = Test(30);
t4 = (Test)30;
//隐式生成临时对象
t4 = 30;
cout << "---------------------" << endl;
//不应该用指针保留一个临时对象,因为出了这个语句后p指向的是一个已经析构的临时对象
//而可以用引用来保留一个临时对象,出了语句临时对象不析构,
//因为引用相当于一个别名,临时对象的生命周期会变成引用变量的生命周期
//const Test* p = &Test(40);
const Test& ref = Test(50);
cout << "---------------------" << endl;
return 0;
}
C++编译器对于对象构造的优化:用临时对象生成新对象的时候,临时对象就不产生了,直接构造新对象就可以了
#include<iostream>
using namespace std;
class Test
{
public:
Test(int a=5,int b=5):ma(a),mb(b)
{
cout << "Test(int,int)" << endl;
}
~Test()
{
cout << "~Test()" << endl;
}
Test(const Test& src) :ma(src.ma), mb(src.mb)
{
cout << "Test(const Test& src)" << endl;
}
void operator=(const Test& src)
{
ma = src.ma;
mb = src.mb;
cout << "operator=" << endl;
}
private:
int ma;
int mb;
};
Test t1(10, 10);
int main()
{
Test t2(20, 20);
Test t3 = t2;
static Test t4 = Test(30, 30);
t2 = Test(40, 40);
t2 = (Test)(50, 50);
t2 = 60;
Test* p1 = new Test(70, 70);
Test* p2 = new Test[2];
//Test* p3 = &Test(80, 80);
const Test& p4 = Test(90, 90);
delete p1;
delete[]p2;
}
Test t5(100, 100);
2. 函数调用过程中对象背后调用的方法
#include<iostream>
using namespace std;
class Test
{
public:
Test(int a = 10) :ma(a)
{
cout << "Test(int)" << endl;
}
~Test()
{
cout << "~Test()" << endl;
}
Test(const Test& t) :ma(t.ma)
{
cout << "Test(const Test& t)" << endl;
}
Test& operator=(const Test& t)
{
cout << "operator=" << endl;
ma = t.ma;
return *this;
}
int getData()const
{
return ma;
}
private:
int ma;
};
Test GetObject(Test t)//不能返回局部的或者临时对象的引用或指针
{
int val = t.getData();
Test tmp(val);
return tmp;
}
int main()
{
Test t1;
Test t2;
t2 = GetObject(t1);
return 0;
}
实参到形参是初始化,因为因为形参还不存在,要调用构造函数,而赋值是两个已存在的互相赋值。
不能返回局部的或者临时对象的引用或指针
3. 对象优化的规则
- 函数参数传递过程中,对象优先按引用传递,不要按值传递
Test GetObject(Test &t)
{
int val = t.getData();
Test tmp(val);
return tmp;
}
-
函数返回对象的时候,应该优先返回一个临时对象,而不要返回一个定义过的对象
-
接受返回值是对象的函数调用的时候,优先按初始化的方式接收,不要按赋值的方式接收
4. CMyString
代码问题
#include <iostream>
using namespace std;
class String
{
public:
String(const char* str = nullptr)
{
cout << "String(const char*)" << endl;
if (str != nullptr)
{
m_data = new char[strlen(str) + 1];
strcpy(m_data, str);
}
else
{
m_data = new char[1];
*m_data = '\0';
}
}
String(const String& src)
{
cout << "String(const String& src)" << endl;
m_data = new char[strlen(src.m_data) + 1];
strcpy(m_data, src.m_data);
}
~String()
{
cout << "~String()" << endl;
delete[]m_data;
m_data = nullptr;
}
//调用String&是为了支持连续的operator=赋值操作
String& operator=(const String& src)
{
cout << "operator=" << endl;
if (&src == this)
{
return *this;
}
delete[]m_data;
m_data = new char[strlen(src.m_data) + 1];
strcpy(m_data, src.m_data);
return *this;
}
const char* c_str() {
return m_data;
}
private:
char* m_data;//用于保存字符串
};
String get_string(String& str) {
const char* pstr = str.c_str();
String tmp(pstr);
return tmp;
}
int main() {
String str1("11111111111111111");
String str2;
str2 = get_string(str1);
cout << str2.c_str() << endl;
return 0;
}
添加带右值引用参数的拷贝构造和赋值函数
左值:有内存、有名字
右值:无内存、无名字(临时量)
一个右值引用变量本身是一个左值
// 临时对象(右值)会匹配到右值引用为参数的函数,这里的src是临时对象(右值)
String(String&& src){
cout << "String(const String&& src)" << endl;
m_data = src.m_data;
src.m_data = nullptr;
}
String& operator=(String&& src){
cout << "operator=(String&& src)" << endl;
if (&src == this)
{
return *this;
}
delete[]m_data;
m_data = src.m_data; // 改变堆区资源指向
src.m_data = nullptr; // 临时对象的指针置空,防止析构的时候释放资源
return *this;
}
CMyString在vector上的应用
5.move和forward
std::move:移动语义,得到右值类型
std::forward:类型的完美转发,得到真实的左/右值
//void construct(T* p, const T& val)//负责对象构造
//{
// new(p)T(val);//定位new
//}
//void construct(T* p, T&& val)//负责对象构造
//{
// new(p)T(std::move(val));//定位new
//}
template<typename Ty2>
void construct(T* p, Ty2&& val)//负责对象构造
{
new (p) T(std::forward<Ty2>(val));//定位new
}
//void push_back(const T& val)//向容器末尾添加元素
//{
// if (full())
// expand();
// //*_last++ = val;//last指针指向的内存构造一个值为val的对象
// _allocator.construct(_last, val);
// _last++;
//}
//void push_back(T&& val)//向容器末尾添加元素
//{
// if (full())
// expand();
// //*_last++ = val;//last指针指向的内存构造一个值为val的对象
// _allocator.construct(_last, std::move(val));
// _last++;
//}
// 函数模板的类型推演 + 引用折叠
// String&& + && = String&&
// String& + && = String&
// T char
// Ty1 String
template<typename Ty1> // 换个名会进行类型推演,直接用T直接断定是右值引用
void push_back(Ty1&& val)
{
if (full())
{
expand();
}
// forward:类型的完美转发
_allocator.construct(_last, std::forward<Ty1>(val));
_last++;
}
函数模板的类型推演 + 引用折叠
String&& + && = String&&
String& + && = String&