自学内容网 自学内容网

【C++】类和对象背后调用的方法

1.创建一个对象背后所调用的方法

#include <iostream>

class Test
{
public:
Test(int a = 10) :ma(a) { std::cout << " Test(int) " << std::endl; }
~Test() { std::cout << " ~Test() " << std::endl; }
Test(const Test& t) : ma(t.ma) { std::cout << " Test(const Test&) " << std::endl; }
Test& operator=(const Test& t)
{ 
ma = t.ma;
std::cout << " Test& operator() " << std::endl; 
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); 是没有区别的
t4 = t2;
// 显式生成临时对象
t4 = Test(30);
t4 = (Test)30;
// 隐式生成临时对象
t4 = 10;
}
class Test
{
public:
Test(int a = 5, int b = 5) : ma(a), mb(b) { std::cout << "Test(int, int)" << std::endl; }
~Test() { std::cout << "~Test()" << std::endl; }
Test(const Test& t) : ma(t.ma), mb(t.mb) { std::cout << "Test(const Test &t)" << std::endl; }
Test& operator=(const Test &t)
{
std::cout << "operator=" << std::endl;
ma = t.ma;
mb = t.mb;
return *this;
}
private:
int ma;
int mb;
};
Test t1(10, 20);// Test(int, int)
int main()
{
Test t2(t1);
Test t(23, 34); // Test(int, int)
//Test t2 = t1;  // Test(const Test & t)
static Test t4 = Test(20, 30);  // Test(int, int)
t2 = Test(40, 40);// Test(int, int)operator=~Test()
// 逗号表达式(50, 50) 取最后一个表达式作为最后的值,即(Test)50
t2 = (Test)(50, 50);// Test(int, int)operator=~Test()
t2 = 60;// Test(int, int)operator=~Test()
Test* p1 = new Test(20, 30);// Test(int, int) 出了语句后不析构 delete的时候堆上的数据才析构
Test* p2 = new Test[2];// Test(int, int)Test(int, int)
// Test* p3 = &Test(32, 40);// Test(int, int)~Test()
const Test& p4 = Test(32, 40);// Test(int, int)
delete p1;// ~Test()
delete []p2;// ~Test()~Test()
return 0;
}
Test t5(24, 90);

        以上的代码,在后面注释中写了,C++在创建对象时所调用了什么构造函数,以及销毁对象时要调用析构函数。

2.函数调用过程中对象背后调用的方法

#include <iostream>
class Test
{
public:
Test(int data = 10) :ma(data)
{
std::cout << "Test(int)" << std::endl;
}
~Test()
{
std::cout << "~Test()" << std::endl;
}
Test(const Test& t) :ma(t.ma)
{
std::cout << "Test(const Test &t)" << std::endl;
}
void operator=(const Test& t)
{
ma = t.ma;
std::cout << "operator=" << std::endl;
}
int getData() const {
return ma;
}
private:
int ma;
};
// 不能返回局部的或者临时对象的指针或引用
Test GetObject(Test t)// 3,Test(const Test&)
{
int val = t.getData();
Test temp(val);// 4.Test(int)
return temp; // 5.在main函数栈帧上构造一个临时对象,使用拷贝构造 Test(const Test&)
// 6.~Test()7.~Test()
}
int main()
{
Test t1;// 1.Test(int)
Test t2;// 2.Test(int)
t2 = GetObject(t1);// 函数调用,实参到形参,是初始化还是赋值呢??它是一个初始化的过程。
// 8. operator=
// 9.~Test
// 10.~Test
// 11.~Test
return 0;
}

        这段代码总共涉及了11个方法的调用。其调用顺序在代码的注释,已经标记出来。

        由上面能够知道,函数调用过程中对象背后调用的方法太多了,我们需要对代码进行优化,来减少这些操作。

所以,我们总结了三条对象优化的规则

  1. 函数参数传递过程中,对象优先按引用传递,不要按值传递。int GetObject(Test &t)
  2. 函数返回对象的时候,应该优先返回一个临时对象,而不要返回一个定义过的对象。return Test(val);
  3. 接收返回值是对象的函数调用的时候,优先按初始化的方式接收,不要按复制的方式接收。Test t2 = GetObject(t1);
#include <iostream>
class Test
{
public:
Test(int data = 10) :ma(data)
{
std::cout << "Test(int)" << std::endl;
}
~Test()
{
std::cout << "~Test()" << std::endl;
}
Test(const Test& t) :ma(t.ma)
{
std::cout << "Test(const Test &t)" << std::endl;
}
void operator=(const Test& t)
{
ma = t.ma;
std::cout << "operator=" << std::endl;
}
int getData() const {
return ma;
}
private:
int ma;
};
Test GetObject(Test &t)
{
int val = t.getData();
return Test(val);
}
int main()
{
Test t1;
Test t2 = GetObject(t1);
    return 0;
}

        还是上面的代码,我们往GetObject函数传入对象的时候,不使用按值传递,使用按引用传递,这样就不会调用拷贝构造函数构造出一个形参,在GetObject返回的时候,我们将temp改成一个Test(val)的临时对象,这样,我们在后面是需要使用临时对象拷贝构造出一个新对象,即Test t1 = Test(val);,这样就相当于Test(val),我们也不再需要调用拷贝构造函数了,而在Test t2的初始化的时候,我们也可以利用需要使用临时对象拷贝构造新对象,优化代码。

        这样子的话,我们从之前的调用了11个方法,优化成了现在只调用了4个方法就解决了问题,极大了提高了程序执行的效率。 

3.带右值引用的拷贝构造函数和赋值重载函数

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <cstring>
using namespace std;
class CMystring
{
public:
CMystring(const char* str = nullptr)
{
cout << "CMystring(const char*)" << endl;
if (str != nullptr)
{
mptr = new char[strlen(str) + 1];
strcpy(mptr, str);
}
else
{
mptr = new char[1];
*mptr = '\0';
}
}
~CMystring()
{
cout << "~CMystring" << endl;
delete[]mptr;
mptr = nullptr;
}
// 带右值引用的拷贝构造
CMystring(CMystring&& str)
{
cout << "CMystring(const CMystring&&)" << endl;
mptr = str.mptr;
str.mptr = nullptr;
}
CMystring(const CMystring& str)
{
cout << "CMystring(const CMystring&)" << endl;
mptr = new char(strlen(str.mptr) + 1);
strcpy(mptr, str.mptr);
}
CMystring& operator=(CMystring&& str)
{
cout << "operator=(const CMystring &&str)" << endl;
if (this == &str)
return *this;
delete[]mptr;
mptr = str.mptr;
str.mptr = nullptr;
return *this;
}
// 带左值引用参数的赋值重载函数
CMystring& operator=(const CMystring& str)
{
cout << "operator=(const CMystring &str)" << endl;
if (this == &str)
return *this;
delete[]mptr;
mptr = new char(strlen(str.mptr) + 1);
strcpy(mptr, str.mptr);
return *this;
}
const char* c_str()const
{
return mptr;
}
private:
char* mptr;
};
CMystring GetString(CMystring& str)
{
const char* pstr = str. c_str();
CMystring tmpStr(pstr);
return tmpStr;
}
int main()
{
CMystring str1("aaaaaaaa");
CMystring str2;
str2 = GetString(str1);
cout << str2.c_str() << endl;
return 0;
}

 

/*
左值:有内存,有名字
右值:没名字(临时量),没内存
*/
int main()
{
// 常量数字,临时量是右值,需要右值引用
// 右值引用变量实际是一个左值
int a = 10;
// int&& c = a; // 错误:无法将右值引用绑定到左值
const int& c = 20;
int&& d = 20;
const CMystring& e = CMystring("aaa");
}

数字常量,临时对象都是右值。


原文地址:https://blog.csdn.net/m0_73839343/article/details/143576379

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!