6

聊聊 C++ 中几类特殊成员函数 - 一线码农

 2 years ago
source link: https://www.cnblogs.com/huangxincheng/p/16468790.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

在 C# 中要说默认给我们定义的特殊成员函数,莫过于 构造函数,但在 C++ 中这样的特殊函数高达 6 种,有必要整合一下聊一聊。

二:特殊成员函数

1. 默认构造函数

和 C# 一样,很多书中都说,如果用户没有定义 构造函数,那么编译器会给我们定义一个,参考下面的例子:


class Person {

public:
	string name;
	int age;
};

int main()
{
	Person person;
}

接下来观察下汇编代码,看下有没有调用 默认构造函数 .


	Person person;
003E32EF  lea         ecx,[person]  
003E32F2  call        Person::Person (03E15EBh)

对于 C# 学习者来说有点懵哈,定义了就相当于new了, 哈哈,这是因为 C++ 默认都是值类型哈,不过这里有必要澄清一下,并不一定所有情况都会调用 默认构造函数,因为 C++ 的汇编生成由各自 编译器 来决定,如果 编译器 觉得没必要调用 构造函数 那它就会把这一步省掉来加速性能,那什么时候不会调呢? 参考如下代码。


class Person {

public:
	void show() {
		printf("show!");
	}
};

int main()
{
	Person person;
	person.show();
}

接下来看下汇编代码。


	person.show();
00E73F4F  lea         ecx,[person]  
00E73F52  call        Person::show (0E713B6h) 

可以清楚的看到,这种情况下调用 构造函数 其实没有必要,所以编译器就干脆省略了。

2. 析构函数

在 C# 中 析构函数 是由 CLR 负责管理,在 C++ 中没有托管这个概念,所以默认只能是结束作用域之前,自动调用 析构函数 释放,参考如下图:

63cc8aea628345de9df318465a48bad7~tplv-k3u1fbpfcp-zoom-1.image

3. 赋值构造函数

刚才也说到了,在 C++ 中甭管是 class 还是 struct 默认都是值类型,既然是值类型就存在stack copy 的情况,在 C# 中也是因为重写了 EqualsGetHashCode 来实现的值copy,接下来简单看下代码:


class Person {

public:
	string name;
	int age;
};

int main()
{
	Person p1 = { "jack",20 };
	Person p2(p1);
}

再看下 Person p2(p1) 的汇编代码。


	Person p2(p1);
000F80A2  lea         eax,[p1]  
000F80A5  push        eax  
000F80A6  lea         ecx,[p2]  
000F80A9  call        Person::Person (0F15C3h) 

从汇编中可以看到调用了 Person::Person (0F15C3h) 函数,请注意,这个不是 构造函数,而是 赋值构造函数 😂, 可以调试下去看看哦。。。 截图如下:

915f1e7a804840cdbe630324ef226747~tplv-k3u1fbpfcp-zoom-1.image

值得说一下的是,C++ 默认提供的 赋值构造函数 是浅copy,如果要实现深 copy 的话,或者有一些自定义的逻辑,建议自己实现一下。


class Person {

public:
	string name;
	int age;

public:
	Person(string name, int age) :name(name), age(age) {}
	Person(const Person& p) {
		name = p.name;
		age = p.age;
	}
};

int main()
{
	Person p1 = { "aaaaaaaaaaaaaaaaaaaaaaaaaaa",20 };
	Person p2(p1);
}

4. 赋值运算符

在 C# 中 值类型 , 匿名类型, Record 都是重写过 Equals= 运算符,所以可以在这些类型上用 =, 其实在 C++ 中也可以在 class 之间进行赋值,因为编译器会帮我们重写运算符 = ,如何看出来呢?先看下代码:


class Person {

public:
	string name;
	int age;

public:
	Person(string name, int age) :name(name), age(age) {}
	Person(const Person& p) {
		name = p.name;
		age = p.age;
	}
};

int main()
{
	Person p1 = { "aaaaaaaaaaaaaaaaaaaaaaaaaaa",20 };
	Person p2 = { "bbbbbbbbbbbbbbbbbbbbbbbbbbb",22 };

	p2 = p1;
}

最后一句的 p2 = p1 之所以能成功是因为 = 被重写了,参考汇编代码。


	p2 = p1;
00FD967C  lea         eax,[p1]  
00FD967F  push        eax  
00FD9680  lea         ecx,[p2]  
00FD9683  call        Person::operator= (0FD161Dh)  

d0a342b8fa824e3fa337e548be5b5db5~tplv-k3u1fbpfcp-zoom-1.image

如果需要自定义,可以自己重写。


class Person {

public:
	string name;
	int age;

public:
	Person(string name, int age) :name(name), age(age) {}
	Person(const Person& p) {
		name = p.name;
		age = p.age;
	}
	Person& operator = (const Person& p) {
		name = p.name;
		age = p.age;
		return *this;
	}
};

int main()
{
	Person p1 = { "aaaaaaaaaaaaaaaaaaaaaaaaaaa",20 };
	Person p2 = { "bbbbbbbbbbbbbbbbbbbbbbbbbbb",22 };

	p2 = p1;
}

在 C++ 11 中还有特殊的 移动构造函数移动赋值构造函数, 这个还需要理解 左值 和 右值引用,篇幅有限,放到后面和大家聊了哈。

图片名称

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK