IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> Effective C++ 条款12:复制对象时勿忘其每一个成分(重要且常用的条款) -> 正文阅读

[C++知识库]Effective C++ 条款12:复制对象时勿忘其每一个成分(重要且常用的条款)

这一篇博客记录我学习Effective C++ term12的学习笔记!

(若日后再次遇到与本term有关的不懂的问题,我都可以再翻越Effective C++?term12看自己记录的notes,再补充上我这一篇博客,那么就ojbk了)

Effective C++ term12 教会我:复制对象时勿忘其每一个成分(重要且常用的条款)

先给出结论:

①Copying函数应该确保复制“对象内的all的成员变量”and“all的base?class的成分”

②不要尝试以某一个copying函数来实现另外一个copying函数,若想消除重复的代码,具体做法就是建立一个新的成员函数给copy构造?和copy?assignment?函数来调用,而这样的函数往往是private的且常常被命名为init。这个策略可以安全地消除copy构造函数和copy?assignment操作符之间的重复代码。

?这两条结论必须要记住!

下面废话不多说,请看以下代码:

(对着注释去看代码,就可以知道本term12所讨论的问题的所在了!)

#include<iostream>
#include<string>
using std::cout;
using std::cin;
using std::endl;
//本.cpp,我们将学习Effective C++ 的条款12
void logCall(const std::string& funcName) //标记函数
{ 

}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          

class Date
{
	//...

};
class Customer
{
public:
	Customer(){}
	//...
	Customer(const Customer& rhs);//拷贝构造
	Customer& operator=(const Customer& rhs);//拷贝赋值
	//...
private:
	std::string m_name;
	//Date lastTransaction;
};
Customer::Customer(const Customer& rhs):m_name(rhs.m_name)
{
	logCall("Customer copy constructor");
}
Customer& Customer::operator=(const Customer& rhs)
{
	logCall("Customer copy assignment operator");
	this->m_name = rhs.m_name;//复制rhs中的数据给this
	return *this;//term10教的代码规范

}
class PriorityCustomer :public Customer
{
public:
	//...
	PriorityCustomer(){}
	//非标准规范的复制对象的代码
	PriorityCustomer(const PriorityCustomer& rhs):priority(rhs.priority)
	{
        //这样的代码看似好像很好,但其实由于PriorityCustomer继承了Customer这个类,
        //因此PriorityCustomer中其实还包含了Customer类的成员变量的副本,而这个副本在这里并没有 
        //被复制
        //因此base class中的成员m_name以及lastTransaction你都没去赋值,让
		logCall("PriorityCustomer copy constructor");
	}
	PriorityCustomer& operator=(const PriorityCustomer& rhs)
	{
		logCall("PriorityCustomer copy assignment operator");
		this->priority = rhs.priority;
		return *this;
	}
	//任何时候,只要你承担起为子类撰写copying函数的重责大任时,必须要很小心的复制其base class的成分
	//而这些成分往往是private的,so你无法直接访问它们,你应该让derived class 的copying函数调用相应的
	//base class 的构造函数
	//比如:(下面就是标准的复制对象时勿忘每个成分的优秀代码)
	PriorityCustomer(const PriorityCustomer& rhs) 
		:Customer(rhs)//调用base class 的拷贝构造函数
		,priority(rhs.priority)
	{
		logCall("PriorityCustomer copy constructor");
	}
	PriorityCustomer& operator=(const PriorityCustomer& rhs)
	{
		logCall("PriorityCustomer copy assignment operator");
		Customer::operator=(rhs);//对base class的成分也进行赋值的动作
		this->priority = rhs.priority;
		return *this;
	}
	//那么现在呢,本term所说的“复制每一个成分”现在应该很clear了
	//请你记住:当你编写一个copying函数时,请确保:
	//①复制all的local成员变量
	//②调用all的base classes内的适当的copying函数
	//也只有这样,你才能在复制对象时把每一个成分都复制到位了!
	// 
	//当然,上述的copying函数的代码都有近似相同的实现本体,若想精益求精,你大可以另外写一个函数,我们一般叫做init函数
	//用于管理重复的代码,来避免代码的重复!
	// 当然,令copy构造函数调用copy assignment函数并不是一个好的选择,反正你别这么干就行了!
	//...
private:
	int priority;

};

知道上述问题之后,我写了个小例子,来告诉自己,以后在写继承时,copying函数(包括copy构造函数以及copy assignment函数)应该如何处理对象的赋值问题了!就是要把对象all的成分都要复制一遍!

请看下面的规范代码:

#include<iostream>
using namespace std;
#include<string>
class Base
{
public:
	Base(){}//默认构造
	Base(int age, string name) :m_Age(age), m_Name(name) 
	{ cout << "this is base class 构造函数" << endl; };
	Base(const Base& b)
	{
		this->m_Age = b.m_Age;
		this->m_Name = b.m_Name;
		cout << "this is base class copy 构造函数" << endl;
	}
	Base& operator=(const Base& b)
	{
		this->m_Age = b.m_Age;
		this->m_Name = b.m_Name;
		cout << "this is base class copy assignment 函数" << endl;
        return *this;
	}
	virtual void showInfo() {}
	int& getAge()
	{
		return this->m_Age;
	}
	string& getName()
	{
		return this->m_Name;
	}

private:
	int m_Age;
	string m_Name;
};
class Son :public Base
{
public:
	Son(){}//默认构造
	Son(double sco, int age, string name):m_score(sco),Base(age,name)
	{
		cout << "this is Son class 构造函数" << endl;
	};
	Son(const Son& s):m_score(s.m_score), Base(s)//拷贝构造函数也是可以用初始化list来出丝滑的
	{	//这里你一上来就用初始化list来给Son类的对象s中的Base class成分赋值,这就是良好的codes!
		//if你不这样干的话当你使用 Son s2 = s1;类似这样拷贝赋值的操作的时候就没法把Base class中的成分赋值过来
		//这样的话你再输出Son s2的all属性的话,其中s2的Base class 属性就会乱码!因为你没有给它们初始化!!!
		cout << "this is Son class copy 构造函数" << endl;
	}
	Son& operator=(const Son& s)
	{
		this->m_score = s.m_score;
		Base::operator=(s);
		cout << "this is Son class copy assignment 函数" << endl;
        return *this;
	}
	virtual void showInfo()
	{
		cout << "分数:" << this->m_score << "\t年龄:" << this->getAge() <<
			"\t姓名:" << this->getName() << endl;
	}

private:
	double m_score;
};
void test()
{
	Son s1(404.0, 32, "lzf");//当然,你需要按照你子类的构造函数的参数顺序来实例化
	Son s2;
	s2 = s1;
	Son s3(s1);
	cout << "s1的Info:" << endl;
	s1.showInfo();
	cout << "s2的Info:" << endl;
	s2.showInfo();
	cout << "s3的Info:" << endl;
	s3.showInfo();
}
int main(void)
{
	test();
	system("pause");
	return 0;
}

上述代码的运行结果(是很漂亮的,很规范的,很准确无误的):

?如果以后你每一个继承的类都能写出如上述的代码,那么你离写出合格的C++代码就一定不远了!!!加油干吧,凡凡!

但,请你注意到这个成员函数!!!

Son(const Son& s):m_score(s.m_score), Base(s)
?? ?{??
?? ??? ?cout << "this is Son class 拷贝函数" << endl;
?? ?}

坑一:这里你一上来就用初始化list来给Son类的对象s中的Base class成分赋值,这就是良好的codes!
if你不这样干的话当你使用 Son s2(s1);类似这样拷贝的操作的时候就没法把Base class中的成分赋值过来,这样的话你再输出Son s2的all属性的话,其中s2的Base class?成员属性就会乱码!因为你没有给它们初始化!!!

比如把上面这个成员函数的代码改成如下:

Son(const Son& s):m_score(s.m_score)
?? ?{??
?? ??? ?cout << "this is Son class 拷贝函数" << endl;
?? ?}

覆盖到原代码中的这个成员函数身上,那么运行结果则是:

?上述所说的乱码逐一体现在运行结果上了,你仔细品即可!

坑二:当然,这里面还有一个坑需要你去注意!那就是:

用一个copying函数去实现另外一个copying函数,这是坑!

(当然,copy?assignment函数不存在这个问题,因为你既然能赋值了那肯定是预先已经存在了的对象才能够给我这个对象做赋值操作!)

比如:Son s1,s2;

s2=s1;

这样ojbk的

这是很荒谬的!这就像是试图去构造一个已经存在了的对象

比如把上面这个成员函数的代码改成如下:

?? ?Son(const Son& s) :m_score(s.m_score)
?? ?{? ??
?? ??? ?Base::Base(s);//在一个copying函数中去调用另一个copying函数
?? ??? ?cout << "this is Son class copy 拷贝函数" << endl;
?? ?}

运行结果:

这里虽然也调用了Base?class?的copy?构造函数,但是这里是把Son的copy构造函数内去调用的Base::Base(s);中的s当做是一个已经存在了的对象,把它里面的Base?class?成分赋值给自己的Base?class?成分,这不就是自己赋值?给自己嘛,本来自己Son3就没初始化,还有用自己的Base?class成分给自己赋值一遍,这显然不会如你所愿!

或者,你可以这样理解,因为这里Son的copy?构造函数中,传入的参数是const Son&?s

有引用的类型,因此你不能再让s赋值为别人(也包括s赋值为自己),也即对于引用类型而言,因为为引用必须在定义的时候初始化,并且不能重新赋值,所以必须要写在初始化列表中,也即

调用Base的copy构造函数初始化Son类对象的Base?class类成分(成员属性)时,必须要写在Son类的copy构造函数中的初始化列表中 !

综上,我相信对于本term12的学习已经足够清晰了!

这里再次给出总结:

①Copying函数应该确保复制“对象内的all的成员变量”and“all的base?class的成分”

(具体做法上述已经说得很clear了!日后忘记的话再回看自己写的这一篇博客即可!)

②不要尝试以某一个copying函数来实现另外一个copying函数,若想消除重复的代码,具体做法就是建立一个新的成员函数给copy构造?和copy?assignment?函数来调用,而这样的函数往往是private的且常常被命名为init。这个策略可以安全地消除copy构造函数和copy?assignment操作符之间的重复代码。

参考:

Effective C++?之条款12

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-26 09:58:18  更:2021-09-26 09:59:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 20:01:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码