杂货边角(16):C++11之后,类的所有构造函数
2018-02-12 17:07
375 查看
C++类声明中,编译器会默认帮助程序员生成一些他们未定义的成员函数,这样的函数被称为“默认函数”,这包括了以下一些自定义类型的成员函数:
1.构造函数;
2.拷贝构造函数
3.拷贝赋值函数(operator= (const Class & T) )
4.移动构造函数
5.移动拷贝赋值函数(operator= (Class&& T) )
6.析构函数
但是程序员一旦在类的定义中显式地声明上述构造函数的任何一种,则编译器将不会在为该类定义该种类下的默认版本。最常见的便是一旦声明了带参构造函数,则同时也需要程序员自己提供无参构造函数,否则该类将不存在无参构造函数。(但不是说定义了构造函数,系统不提供拷贝构造函数、移动构造函数等,即上述1、2、 3等之间彼此不冲突)。
1.构造函数;
2.拷贝构造函数
3.拷贝赋值函数(operator= (const Class & T) )
4.移动构造函数
5.移动拷贝赋值函数(operator= (Class&& T) )
6.析构函数
但是程序员一旦在类的定义中显式地声明上述构造函数的任何一种,则编译器将不会在为该类定义该种类下的默认版本。最常见的便是一旦声明了带参构造函数,则同时也需要程序员自己提供无参构造函数,否则该类将不存在无参构造函数。(但不是说定义了构造函数,系统不提供拷贝构造函数、移动构造函数等,即上述1、2、 3等之间彼此不冲突)。
#include <string> #include <iostream> #include <memory> #include <vector> #include <string.h> using namespace std; class CMyString { private: char * buf; int len; private: void copy(const char* s) { buf = new char[len+1]; memcpy(buf, s, len); buf[len] = '\0'; } public: CMyString() { std::cout << "non-param constructor" << std::endl; buf = nullptr; len = 0; } CMyString(const char* str = nullptr) { if (str == nullptr) { std::cout << "one-param constructor" << std::endl; buf = nullptr; len = 0; } else { std::cout << "one-param constructor: " << str << std::endl; len = strlen(str); copy(str); } } CMyString(const CMyString& str) { //拷贝构造函数 std::cout << "copy constructor: " << str.buf << std::endl; len = str.len; copy(str.buf); } CMyString(CMyString&& str) { std::cout << "move constructor: " << str.buf << std::endl; //移动构造函数 //也可以直接使用std::move len = str.len; buf = str.buf; str.len = 0; str.buf = nullptr; } CMyString& operator=(const CMyString& str) { std::cout << "copy and assignment func: " << str.buf << std::endl; //拷贝赋值函数 if (&str != this) { if (buf != nullptr) { delete[] buf; buf = nullptr; } len = str.len; copy(str.buf); } return *this; } CMyString& operator=(CMyString&& str) { std::cout << "move and assignment func: " << str.buf << std::endl; //移动赋值函数 if (this != &str) { if (buf != nullptr) { delete[] buf; buf = nullptr; } len = str.len; buf = str.buf; str.len = 0; str.buf = nullptr; } return *this; } ~CMyString() { if (buf == nullptr) { std::cout << "destructor" << std::endl; } else { std::cout << "destructor: " << buf << std::endl; delete[] buf; buf = nullptr; } } void print() { if (buf != nullptr) std::cout << buf << std::endl; else std::cout << "buf is null" << std::endl; } }; void func1(CMyString str) { } CMyString func2() { CMyString s("34"); std::cout << "Resource from " << __func__ << ": " << hex << &s << endl; return s; } void func3(CMyString && str) { CMyString tmp = std::move(str); //move constructor //CMyString tmp = str; //copy constructor这是因为虽然str初始声明为右值引用,但一旦 //占据表达式右值的位置时,则该右值引用的语义将变成不折不扣的左值,除非用move强制声明 } void test0() { CMyString s1("12"); func1(s1); //对象str尚未初始化,会调用拷贝构造函数 CMyString s2(func2()); // 对象s2尚未初始化,会产生临时对象,调用移动构造函数 std::cout << "Resource from " << __func__ << ": " << std::hex << &s2 << endl; //这一步存在一步编译器优化,比移动语义更加激进的优化方式,直接将func2()返回的temp对象 //的指针,直接赋给s2,故这一步实际并没有调用任何构造函数RVO return value optimization //CMyString s2(std::move(func2())); //这样写则会体现出移动语义的鸠占鹊巢,存在临时对象的构造和析构 CMyString s3 = "56";//对象s3尚未初始化,虽然是有'=6',但是其实依旧调用带参构造函数 s3 = s1; //对象s3已初始化,会调用拷贝赋值函数 func3(static_cast<CMyString&&> (s1) ); s1.print(); cout << "now comes to the destructor part:" << endl; } void test1() { CMyString s4 = "78"; std::vector<CMyString> v1; //v1.push_back(s4); v1.push_back(std::move(s4)); // 对象尚未初始化,会调用移动构造函数 std::cout << "v1 vector-container list:\n"; for (auto& str : v1) str.print(); std::vector<CMyString> v2; v2 = std::move(v1); std::cout << "v1 content:\n"; for (auto& str : v1) str.print(); std::cout << "v2 content:\n"; for (auto& str : v2) str.print(); } void test2() { CMyString s5 = "9"; s5 = func2(); // 对象s5已初始化, 会产生临时对象,调用移动赋值函数 } int main(void) { std::cout << "begin test0()" << std::endl; test0(); std::cout << std::endl; std::cout << "begin test1()" << std::endl; test1(); std::cout << std::endl; std::cout << "begin test2()" << std::endl; test2(); return 0; }
相关文章推荐
- 杂货边角(14):C++11在构造函数方面的新特性
- C++11标准之后的移动构造函数和复制构造函数探究
- 深入解析父子线程(父子线程相互独立,子线程使用自己的栈空间,进程要等到所有线程终止运行之后才终止运行)
- Android API Level在11前后及16之后时Notification的不同用法
- C++学习笔记18,C++11中的初始化列表构造函数(二)
- C++11 FAQ中文版:继承的构造函数
- |1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20所有这些字符串,把它的数字一个个剥离??
- C++学习笔记16,C++11中的显式的默认构造函数以及显示删除默认构造函数
- 安装Fedora 16之后的那些事(持续更新)
- replace 如何分别替换第一次匹配和所有匹配之后得到的字符串
- C++学习笔记16-2 编译器对构造函数的调用处理
- 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
- 【面试系列】把一个字符串里除了大小写字母以外的所有东西都去掉,然后都变成小写,之后按顺序排序
- 使用DOM的方法获取所有li元素,然后使用jQuery()构造函数把它封装为jQuery对象
- effective C++笔记之条款16: 在operator=中对所有数据成员赋值
- C++学习笔记19,C++11的委托构造函数(三)
- opencl:C++11下使用别名(x,y,z,hi,lo...)访问vector类型(cl_int2,cl_long16...)的元素
- linux中修改环境变量之后导致系统崩溃(用户所有命令不能使用)的解决办法
- android开发(16) 遍历所有的已经安装的应用程序,启动其他的应用程序
- [django]启动之后所有请求都是500错误