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

虚幻大学 xuhss 162℃ 0评论

? 优质资源分享 ?

学习路线指引(点击解锁) 知识定位 人群定位
? Python实战微信订餐小程序 ? 进阶级 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
?Python量化交易实战? 入门级 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统

一:背景

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t90ftep9-1657600937158)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/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) 函数,请注意,这个不是 构造函数,而是 赋值构造函数 ?, 可以调试下去看看哦。。。 截图如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BKhIIeZq-1657600937162)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/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)  

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xjf1b1OF-1657600937163)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/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 中还有特殊的 移动构造函数移动赋值构造函数, 这个还需要理解 左值 和 右值引用,篇幅有限,放到后面和大家聊了哈。

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

转载请注明:xuhss » 聊聊 C++ 中几类特殊成员函数

喜欢 (0)

您必须 登录 才能发表评论!