一:背景

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

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) 函数,请注意,这个不是 构造函数,而是 赋值构造函数 , 可以调试下去看看哦。。。 截图如下:

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

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


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++ 中几类特殊成员函数的更多相关文章

  1. VB6/VBA中跟踪鼠标移出窗体控件事件(类模块成员函数指针CHooker类应用)

    一.关于起因 前几天发了一篇博文,是关于获取VB类模块成员函数指针的内容(http://www.cnblogs.com/alexywt/p/5880993.html):今天我就发一下我的应用实例. V ...

  2. 在类的成员函数中调用delete this

    最近面试的时候被问到一个问题是,在C++中,能否在类的成员函数中调用delete this,后来网上查了一下资料,关于这个问题说得比较好的有http://blog.sina.com.cn/s/blog ...

  3. [转载]能不能同时用static和const修饰类的成员函数?

    题目(一):我们可以用static修饰一个类的成员函数,也可以用const修饰类的成员函数(写在函数的最后表示不能修改成员变量,不是指写在前面表示返回值为常量).请问:能不能同时用static和con ...

  4. (转)c++类的成员函数存储方式(是否属于类的对象)---一道面试题引发的思考

    昨天去面试一家公司,面试题中有一个题,自己没弄清楚,先记录如下: class D { public: void printA() { cout<<"printA"< ...

  5. C++类的成员函数使用的一些小总结

    From: http://blog.csdn.net/xiayefanxing/article/details/7607506 这一阵做项目代码开发的时候,用到了在一个C++文件中使用另一个类的成员函 ...

  6. C++ 类的成员函数指针 ( function/bind )

    这个概念主要用在C++中去实现"委托"的特性. 但现在C++11 中有了 更好用的function/bind 功能.但对于类的成员函数指针的概念我们还是应该掌握的. 类函数指针 就 ...

  7. C++:类的成员函数定义方式

    1.成员函数的第一种定义方式:在类声明中只给出成员函数的原型,而将成员函数的定义 放在类的外部. 返回值类型 类名::成员函数名(参数表) {      函数体  } class Point{ pub ...

  8. C++类的成员函数(在类外定义成员函数、inline成员函数)

    类的成员函数(简称类函数)是函数的一种,它的用法和作用和前面介绍过的函数基本上是一样的,它也有返回值和函数类型,它与一般函数的区别只是:它是属于一个类的成员,出现在类体中.它可以被指定为private ...

  9. C++学习之路—运算符重载(二)运算符重载作为类的成员函数和友元函数

    (根据<C++程序设计>(谭浩强)整理,整理者:华科小涛,@http://www.cnblogs.com/hust-ghtao转载请注明) 对运算符重载的函数有两种处理方式:(1)把运算符 ...

随机推荐

  1. 调试F9/F10/F11/F8

    这篇随笔记录来自于实现活动促销页弹幕过程学习: // 页面加载完初始化方法$(function () { GetCustList(); createBarrage(); }) // 某功能的初始化方法 ...

  2. Golang 函数 方法 接口的简单介绍

    函数 函数是基本的代码块,通常我们会将一个功能封装成一个函数,方便我们调用,同时避免代码臃肿复杂. 函数的基本格式 func TestFunc(a int, b string) (int, strin ...

  3. 『忘了再学』Shell基础 — 14、环境变量(二)

    目录 1.PS1变量的作用 2.PS1变量的查看 2.PS1可以支持的选项 3.PS1环境变量的配置 4.总结 提示: 在Linux系统中,环境变量分为两种.一种是用户自定义的环境变量,另一种是系统自 ...

  4. Tenseal库

    在此记录Tenseal的学习笔记 介绍 在张量上进行同态计算的库,是对Seal的python版实现,给开发者提供简单的python接口,无需深究底层密码实现. 当前最新版本:3.11 位置:A lib ...

  5. 搞定了!OAuth2使用验证码进行授权

    现在验证码登录已经成为很多应用的主流登录方式,但是对于OAuth2授权来说,手机号验证码处理用户认证就非常繁琐,很多同学却不知道怎么接入. 认真研究胖哥Spring Security OAuth2专栏 ...

  6. elemetnUI表格分别给列表每一个按钮加loading

    // 获取列表数据的时候--添加按钮loading this.list = this.list.map((item) => { this.$set(item, "dataLoading ...

  7. 在linux上开启酸酸乳,未完待续

    在服务器调试深度学习环境的时候总需要下载conda的包,一直以来都觉得是因为国内访问慢,于是想在服务器上开 ,或者ssr.由于过去用ssr多一些,于是想了解ssr on linux. 1.首先win1 ...

  8. MySQL、SqlServer、Oracle,这三种数据库的优缺点,你知道吗?

    盘点MySQL.SqlServer.Oracle 三种数据库优缺点 MySQL SqlServer Oracle 一.MySQL 优 点 体积小.速度快.总体拥有成本低,开源:支持多种操作系统:是开源 ...

  9. CabloyJS实现了一款基于X6的工作流可视化编辑器

    介绍 文档演示:CMS审批工作流演示了如何通过JSON来直接创建一个工作流定义,通常用于为具体的业务数据生成预定义或内置审批工作流的场景 CabloyJS 4.8.0采用X6 图编辑引擎实现了一款工作 ...

  10. 一个全新的Vue拖拽特性实现:“移动”部分

    关于拖拽 CabloyJS提供了完备的拖拽特性,可以实现移动和调整尺寸两大类功能,这里对移动的开发进行阐述 关于调整尺寸的开发,请参见:拖拽:调整尺寸 演示 开发步骤 下面以模块test-party为 ...