龙空技术网

C++构造函数总结

神秘的程序员 104

前言:

此刻我们对“有参构造函数”大约比较关心,看官们都想要剖析一些“有参构造函数”的相关知识。那么小编同时在网上汇集了一些对于“有参构造函数””的相关文章,希望我们能喜欢,姐妹们一起来了解一下吧!

最近在找工作,比较忙,所以没有时间写文章了。找了一段时间了,还是没有什么收获。找工作给我一个最大的体会就是"基础要扎实,代码能力要强(这里的代码不是指那种业务逻辑的代码哦)"。

01 文章概要

这篇文章总结一下C++中的构造函数,然后自己实现一个MyString类,实现对MyString字符串的输入和输出。如果对C++熟悉的童鞋应该就能猜到后面要介绍的知识了:

C++的构造函数(分的细就是6种,粗一点就是4种)

介绍的过程中,不会对所有的都介绍的很细,不然就要花太多的时间啦。

02 C++中的构造函数分类

C++中的构造函数主要有下面几种:

默认构造函数带参构造函数拷贝构造函数转换构造函数显式构造函数移动构造函数

其中转换构造函数、显式构造函数、拷贝构造函数、移动构造函数需要仔细讲一下。

什么是转换构造函数和显式构造函数?

看下面一段代码:

#include <iostream>#include <string>using namespace std;class Dog {public:	Dog() {}	Dog(const char* _name): name(_name) {}	public:	string name;};int main() {	Dog dog = "Hei";	cout << dog.name << endl;	return 0;}

经过验证,这里代码是可以正常编译运行的,看一下16行: 你是不是很疑惑,为什么可以将一个常量字符串赋值为Dog类型的对象呢?

其实编译器自动做了隐式类型转换,调用了转换构造函数了,所以程序没有问题。所以转换构造函数的定义就出来了:只含有一个参数,并且该参数不是类的const引用成员,这个构造函数就是转换构造函数。

但是,这样的写法太让人迷惑了,你也可以在构造函数前面加上explicit关键字,声明该构造函数不允许被隐式调用(第8行),然后上面的程序编译就会报错了。explitcit的用法如下:

explicit只能用于构造函数,禁止构造函数被隐式调用explicit只有作用于单参构造函数才有作用,因为其它的构造函数都是显式调用的用explicit声明的构造函数称为显式构造函数

什么是拷贝构造函数和移动构造函数?

拷贝构造函数顾名思义就是在发生对象拷贝的时候调用的了,一般拷贝构造函数的写法如下(为什么我就不解释了,看不明白的需要补一下C++基础了):

 MyString(const MyString& str) {    	cout << "The copy constructor is callback." << endl;        if(str.len == 0) {            return;        }        len = str.len;        data = new char[len+1];        memcpy(data, str.data, len);        data[len] = '\0';    }

所以,拷贝构造函数在进行对象拷贝的时候采用的方式是"深拷贝",啥意思? 就是我申请一个新的内存空间,然后把你的内容全部拷贝在我自己的内容空间里面,然后你做了任何修改对我都没有影响,拷贝的很彻底。与深拷贝的对应还有浅拷贝,有兴趣的自己去查一下吧,这里就不多说了。

深拷贝有自己的优点,但是也有缺陷。如果我们在拷贝临时对象的时候,还是采用深拷贝的话,那么我就要: 申请内存、拷贝内容、释放临时对象。这样效率还是比较低的。

C++11为了解决临时对象的拷贝效率的问题,引入了右值引用和移动语义。

妈的,都是什么鬼? 如果要细讲的话,这一篇文章估计百分之八十的内容都是关于它的了,所以这一篇文章就不细讲了。只大概的介绍一下。

所谓的左值和右值,本质区别是能够取得它们的地址。但是我觉得你可以简单的理解一下:

左值是表达式结束了之后仍然存在的持续化变量,比如a = 3; 表达式结束了,a仍然还存在。右值是表达式结束了之后就消失的临时变量,比如a = 1+2;1+2就是个临时变量。

有什么用呢? 先来看一下移动拷贝构造函数的写法:

MyString(MyString&& str) {    	cout << "The move constructor is callback." << endl;        len = str.len;        data = str.data;        str.len = 0;        str.data = nullptr;    }

上面的移动拷贝构造函数仍然是拷贝str,但是没有做深拷贝,只是拷贝了str的data指针,然后将str.data置为空。相当于我把str的data的内容偷了过来。这样,就避免了内存空间的申请和拷贝的效率,效率自然就高了。

移动构造函数的参数是一个右值引用,因此在参数传入时要将左值转换为右值,使用std::move()来实现。

值得注意的是,移动语义改善了临时对象拷贝的效率,由于把临时对象内部的指针变量偷了过去,所以经移动拷贝之后,临时对象就失效了。

03 MyString类的代码实现

class MyString {public:    // 默认构造函数    MyString(): data(nullptr), len(0){     	cout << "default constructor is callback." << endl;    }    // 带参构造函数    MyString(const char* _data) {        len = strlen(_data);    // strlen(_data)返回字符串_data的长度,不包含'\0'        data = new char[len+1];        memcpy(data, _data, len);        data[len] = '\0';    }    // 拷贝构造函数    MyString(const MyString& str) {    	cout << "The copy constructor is callback." << endl;        if(str.len == 0) {            return;        }        len = str.len;        data = new char[len+1];        memcpy(data, str.data, len);        data[len] = '\0';    }    // 拷贝赋值函数    MyString& operator=(const MyString& str) {    	cout << "The copy = function is callback." << endl;        if(this == &str) {            return *this;        }        len = str.len;        data = new char[len+1];        memcpy(data, str.data, len);        data[len] = '\0';        return *this;    }    // 移动构造函数    MyString(MyString&& str) {    	cout << "The move constructor is callback." << endl;        len = str.len;        data = str.data;        str.len = 0;        str.data = nullptr;    }    // 移动赋值函数    MyString& operator=(MyString&& str) {    	cout << "The move = function is callback." << endl;        if(this == &str) {            return *this;        }        len = str.len;        data = str.data;    // 浅拷贝,直接将内存移动过来        str.len = 0;        str.data = nullptr; // 移动拷贝之后,临时对象失效了        return *this;    }    // 析构函数    virtual ~MyString() {        len = 0;        if(data) {            delete[] data;            data = nullptr;        }    }    // 重载>>    friend istream& operator>>(istream& in, MyString& str) {        string tempstr;        getline(cin, tempstr, '\n');        str.len = tempstr.length();        str.data = new char[str.len+1];        memcpy(str.data, tempstr.c_str(), tempstr.length());        str.data[str.len] = '\0';        return in;    }    // 重载<<    friend ostream& operator<<(ostream& out, MyString& str) {        out << str.data;        return out;    }private:    char* data;    size_t len; //字符串的长度,真实长度=len+1,最后一个字符为'\0'};int main() {	MyString str1, str2, str3;						// 调用默认构造函数	cin >> str1 >> str2 >> str3;		MyString str_1_copy(str1);						// 调用拷贝构造函数	cout << "str_1_copy is: " << str_1_copy << endl;		MyString str_2_copy(std::move(str2));			// 调用移动拷贝构造函数	cout << "str_2_copy is: " << str_2_copy << endl;		MyString str4, str5;	str4 = str1;									// 调用赋值构造函数	cout << "str4 is: " << str4 << endl;		str5 = std::move(str3);	cout << "str5 is: " << str5 << endl;			// 调用移动赋值构造函数  return 0;}

运行结果分析如下:

运行结果

04 小结

这篇文章主要还是介绍C++中的构造函数,对于C++11中引入的移动语义介绍的比较少,恰巧我个人觉得C++中的移动语义和完美转发这两个问题也是比较难以理解的概念,所以在这里先埋下一个伏笔,后面有时间了再专门用一到两篇文章介绍移动语义std::move()。

如果C++基础比较薄弱的童鞋不理解也没有关系,就当个玩笑看一下,先去补一下C++基础。移动语义这个概念,研究生期间做项目全是用的C++也没有遇到。

今天的内容就到这儿了。如果对我的推|文有兴趣,欢迎转、载分、享。也可以推荐给朋友关、注哦。只推干货,宁缺毋滥。

标签: #有参构造函数