在C++98中有左值和右值的概念,不过这两个概念对于很多程序员并不关心,因为不知道这两个概念照样可以写出好程序。在C++11中对右值的概念进行了增强,我个人理解这部分内容是C++11引入的特性中最难以理解的了。该特性的引入至少可以解决C++98中的移动语义和完美转发问题,若你还不清楚这两个问题是什么,请向下看。
温馨提示,由于内容比较难懂,请仔细看。C++已经够复杂了,C++11中引入的新特性令C++更加复杂了。在学习本文的时候一定要理解清楚左值、右值、左值引用和右值引用。
移动构造函数
首先看一个C++98中的关于函数返回类对象的例子。
class MyString { public: MyString() { _data = nullptr; _len = 0; printf("Constructor is called!\n"); } MyString(const char* p) { _len = strlen (p); _init_data(p); cout << "Constructor is called! this->_data: " << (long)_data << endl; } MyString(const MyString& str) { _len = str._len; _init_data(str._data); cout << "Copy Constructor is called! src: " << (long)str._data << " dst: " << (long)_data << endl; } ~MyString() { if (_data) { cout << "DeConstructor is called! this->_data: " << (long)_data << endl; free(_data); } else { std::cout << "DeConstructor is called!" << std::endl; } } MyString& operator=(const MyString& str) { if (this != &str) { _len = str._len; _init_data(str._data); } cout << "Copy Assignment is called! src: " << (long)str._data << " dst" << (long)_data << endl; return *this; } operator const char *() const { return _data; } private: char *_data; size_t _len; void _init_data(const char *s) { _data = new char[_len+1]; memcpy(_data, s, _len); _data[_len] = ''; } }; MyString foo() { MyString middle("123"); return middle; } int main() { MyString a = foo(); return 1; } |
该例子在编译器没有进行优化的情况下会输出以下内容,我在输出的内容中做了注释处理,如果连这个例子的输出都看不懂,建议再看一下C++的语法了。我这里使用的编译器命令为g++ test.cpp -o main -g -fno-elide-constructors,之所以要加上-fno-elide-constructors选项时因为g++编译器默认情况下会对函数返回类对象的情况作返回值优化处理,这不是我们讨论的重点。