0
点赞
收藏
分享

微信扫一扫

聊聊 C++ 中几类特殊成员函数

小编 2022-09-27 阅读 148

一:背景

在 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++ 中没有托管这个概念,所以默认只能是结束作用域之前,自动调用 ​​析构函数​​ 释放,参考如下图:

聊聊 C++ 中几类特殊成员函数_c++

3. 赋值构造函数

刚才也说到了,在 C++ 中甭管是 class 还是 struct 默认都是值类型,既然是值类型就存在​​stack copy​​​ 的情况,在 C# 中也是因为重写了 ​​Equals​​​ 和 ​​GetHashCode​​ 来实现的值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)​​​ 函数,请注意,这个不是 ​​构造函数​​​,而是 ​​赋值构造函数​​ 😂, 可以调试下去看看哦。。。 截图如下:

聊聊 C++ 中几类特殊成员函数_构造函数_02

值得说一下的是,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)

聊聊 C++ 中几类特殊成员函数_c#_03

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

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 中还有特殊的 ​​移动构造函数​​​ 和 ​​移动赋值构造函数​​, 这个还需要理解 左值 和 右值引用,篇幅有限,放到后面和大家聊了哈。

聊聊 C++ 中几类特殊成员函数_构造函数_04



举报

相关推荐

0 条评论