浅谈类的几个基础构造函数
2018-11-20 03:15:03来源:博客园 阅读 ()
通过例子来介绍下C++类的几个基础构造函数。
我们以一个C类型的字符串为例:
class myString { public: myString(const char* rhs = 0); // 默认(含参)构造函数 myString(const myString& rhs); // 拷贝构造函数 myString(myString&& rhs) noexcept; // 移动构造函数 myString& operator=(const myString& rhs); // 拷贝赋值函数 myString& operator=(myString&&) noexcept; // 移动赋值函数 ~myString(); // 析构函数 private: char* m_data; };
(一)、我们定义一个myString类,仅包含一个char* 的指针。先来看看它的默认构造函数:
inline myString::myString(const char* rhs) { if (rhs) { m_data = (char*)new char[strlen(rhs) + 1]; strcpy_s(m_data,strlen(rhs)+1, rhs); } else { m_data = new char[1]; *m_data = '\0'; } }
这里仅是申请了一块内存,对传入字符串进行了拷贝。
(二)、关于拷贝构造函数。拷贝构造函数是仅是对于传入对象的一次深拷贝。记得使用引用传入,由于我们不需要对传入对象进行修改操作,那就对它声明为const吧。
inline myString::myString(const myString& rhs) { m_data = (char*)new char[strlen(rhs.m_data) + 1]; strcpy_s(m_data, strlen(rhs.m_data)+1, rhs.m_data); }
(三)、对于拷贝赋值函数我们尤其要注意自我赋值问题。如果我们不进行自我赋值检测,即传入对象和被赋值对象是同一个的话,当delete完之后,传入的对象也已经不存在了,这并不是我们想要的结果。
inline myString& myString::operator=(const myString& rhs) { if (this != &rhs) { if (m_data) delete m_data; m_data = (char*)new char[strlen(rhs.m_data) + 1]; strcpy_s(m_data, strlen(rhs.m_data)+1, rhs.m_data); }
return *this; }
(四)、关于移动构造函数。移动构造函数给我带来一种 “ 偷 ” 的概念。如何理解呢?我们来列举2个移动构造函数的主要应用场景:1. 假设我们需要将一批myString对象存入vector,当vector由于原容量不够大而发生扩充时,之前的C++版本中vector内部会重新申请一块内存,然后把之前存储的对象一个一个拷贝到新内存上,并且释放原内存。
当C++11以后我们可以借助移动构造函数这个“偷”的概念。怎么偷? 先看下代码:
inline myString::myString(myString&& rhs) noexcept : m_data(rhs.m_data) { rhs.m_data = NULL; }
这不就是指针的拷贝,换言之浅拷贝吗? 可以这么说!既然原先的对象可以被拿来用,我们又何必大费周章先做一份拷贝,再删除原副本呢?这换来的是效率上的巨大提升。使用移动构造函数我们需要注意2点:1). 不能让移动构造函数抛出异常,我们将它设为noexcept; 2). “ 偷 ”完东西将原指针设为NULL, 否则要是原对象被delete,“ 偷 ”的东西也就没了,这让我们难以接受。2. 如果我们要将一个容器拷贝到另一个容器,将容器内的对象一个一个拷贝?天哪!我们还是来 “ 偷 ” 吧。C++11以后容器都内置有移动构造函数,当我们对容器进行拷贝时,它已经在背后悄悄地 “ 偷 ”了。(举个例子, 将一个300万个对象的vector进行拷贝, 是一个一个拷贝好呢, 还是只需要“ 偷 ” 3个指针好呢(start, finish, end_of_storage)? 果然还是 “ 偷 ” 起来爽呀)
(五)、移动赋值函数。移动赋值的原理同上,也是采用 “ 偷 ” 的方法,尤其注意自我赋值即可。
inline myString& myString::operator= (myString&& rhs) noexcept { if (this != &rhs) { if (m_data) delete m_data; m_data = rhs.m_data; rhs.m_data = NULL; } return *this; }
(六)、析构函数。析构函数的任务就是把申请的对象进行释放。
inline myString::~myString() { delete m_data; }
这里给出测试代码:
(我们对一些代码加了些提示性的语句。 测试环境: VS2017)
#include <iostream> #include <cstring> #include <vector> using namespace std; class myString { public: myString(const char* rhs = 0); myString(const myString& rhs); myString(myString&& rhs) noexcept; myString& operator=(const myString& rhs); myString& operator=(myString&&) noexcept; ~myString(); char* getStr() { return m_data; } private: char* m_data; }; inline myString::myString(const char* rhs) { if (rhs) { m_data = (char*)new char[strlen(rhs) + 1]; strcpy_s(m_data,strlen(rhs)+1, rhs); } else { m_data = new char[1]; *m_data = '\0'; } } inline myString::myString(const myString& rhs) { m_data = (char*)new char[strlen(rhs.m_data) + 1]; strcpy_s(m_data, strlen(rhs.m_data)+1, rhs.m_data); } inline myString::myString(myString&& rhs) noexcept : m_data(rhs.m_data) { rhs.m_data = NULL; cout << " 调用了我一次。myString(myString&& rhs) " << endl; } inline myString& myString::operator=(const myString& rhs) { if (this != &rhs) { if (m_data) delete m_data; m_data = (char*)new char[strlen(rhs.m_data) + 1]; strcpy_s(m_data, strlen(rhs.m_data)+1, rhs.m_data); } return *this; } inline myString& myString::operator=(myString&& rhs) noexcept { if (this != &rhs) { if (m_data) delete m_data; m_data = rhs.m_data; rhs.m_data = NULL; } cout << " 调用了我一次。operator(myString&& rhs) " << endl; return *this; } inline myString::~myString() { delete m_data; } int main() { myString str1; myString str2("wang"); myString str3(str2); myString str4 = str2; vector<myString> vec; int n = 20; while (n--) // 通过size 和 capacity 的值以及辅助性语句,查看容器扩充时是否调用移动拷贝。 { cout << "vector size = "; cout << vec.size() << endl; cout << "vector capacity = " ; cout << vec.capacity() << endl; cout << endl; vec.push_back(str1); cout << "vector size = "; cout << vec.size() << endl; cout << "vector capacity = "; cout << vec.capacity() << endl; cout << endl; vec.push_back(str2); cout << "vector size = "; cout << vec.size() << endl; cout << "vector capacity = "; cout << vec.capacity() << endl; cout << endl; vec.push_back(str3); cout << "vector size = "; cout << vec.size() << endl; cout << "vector capacity = "; cout << vec.capacity() << endl; cout << endl; vec.push_back(str4); cout << "vector size = "; cout << vec.size() << endl; cout << "vector capacity = "; cout << vec.capacity() << endl; } vector<myString> vec2{ vec }; // 查看容器赋值时是否调用内部移动构造(无提示性语句。 可通过vs2017调试跟踪函数调用过程) return 0; }
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:函数内存分配
- 如何0基础学习C/C++? 2020-06-06
- 复习C++语法--基础篇 2020-05-27
- C++基础 学习笔记六:复合类型之数组 2020-04-25
- C++基础 学习笔记五:重载之运算符重载 2020-04-23
- C++基础 学习笔记四:重载之函数重载 2020-04-22
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash