当前位置:首页 > 科技  > 软件

理解C++之构造函数

来源: 责编: 时间:2023-11-28 17:12:18 356观看
导读相信做过Java、C++或者其他面向对象语言开发的朋友们一定对构造函数这个概念不陌生。以前初学C++的时候笔者看过几次《C++ Primer》这本书,但是每次都是走马观花式的快速阅读, 每次浏览完之后内心就会冒出两个字:就这?现

相信做过Java、C++或者其他面向对象语言开发的朋友们一定对构造函数这个概念不陌生。以前初学C++的时候笔者看过几次《C++ Primer》这本书,但是每次都是走马观花式的快速阅读, 每次浏览完之后内心就会冒出两个字:就这?现如今回想起来真是图样图森破 。eo028资讯网——每日最新资讯28at.com

学习最忌讳的就是心急如焚,砍柴不磨刀,所谓欲速则不达,一步一个脚印才能走得更稳。eo028资讯网——每日最新资讯28at.com

eo028资讯网——每日最新资讯28at.com

由问题开始

下面我们就从几个问题出发,加深一下对C++中构造函数的了解:eo028资讯网——每日最新资讯28at.com

1、构造函数初始化与赋值的问题

以下的这两个写法有什么区别?eo028资讯网——每日最新资讯28at.com

class Person {public:    Person(const string name, int age);private:    string name;    int age;};// 第一种写法Person::Person(const string name, int age) {    this->name = name;    this->age = age;}// 第二种写法Person::Person(const string name, int age):name(name),age(age) {    }

在这个例子中第二种写法是使用构造函数初始值的写法,第一种写法虽然合法,也没有错误,但是并不是合理的写法,并不推荐。eo028资讯网——每日最新资讯28at.com

那么这两种写法有什么区别呢? 第一种写法会经历先初始化,再赋值这么两个过程;而第二种写法则是直接初始化数据成员一步到位。所以这里面会存在一个效率的问题,第二种写法的效率更高。eo028资讯网——每日最新资讯28at.com

我们再看一个例子,如果我们把类的成员使用const修饰呢,结果会怎样?eo028资讯网——每日最新资讯28at.com

class Person {public:    Person(const string name, int age);private:    string name;    const int age;};// 第一种写法,编译报错Person::Person(const string name, int age) {    this->name = name;    this->age = age;}// 第二种写法Person::Person(const string name, int age):name(name),age(age) {}

我们发现第一种写法行不通了,不能编译通过了,这是因为age被const修饰了,必须在初始化时赋值,所以第一种写法就不行了,由此看出使用构造函数初始值的写法更加规范,更加安全。eo028资讯网——每日最新资讯28at.com

建议:在《Effective C++》一书中的第4条"确定对象被使用前已先被初始化"中也强调了绝对必要使用构造函数初始值eo028资讯网——每日最新资讯28at.com

2、成员变量的初始化顺序

如下例子,如果外部调用Point对象的getX方法,能拿到正确的值吗?答案是不能的,因为成员x比成员y先初始化。eo028资讯网——每日最新资讯28at.com

class Point {public:    Point(int x, int y);    int getX() const{        return x;    }    int getY() const{        return y;    }private:    int x;    int y;};// 本意是把 yVal的值赋值给成员变量y,然后把成员变量y的值赋值给成员变量xPoint::Point(int xVal, int yVal):y(yVal),x(y) {}

一般按照我们常规的思维,我们在构造函数中先写了y,再x,那应该是写初始化y,再初始化x吧?然而事实并不是这样子的。eo028资讯网——每日最新资讯28at.com

起始构造函数初始值是有一定的规则的:eo028资讯网——每日最新资讯28at.com

构造函数初始值列表只说明用于初始化成员的值,而不限定初始化的具体执行顺序。成员的初始化顺序与它们在类定义中的出现顺序一致:第一个成员先被初始化, 然后第二个,以此类推。构造函数初始值列表中初始值的前后位置关系不会影响实际的初始化顺序。eo028资讯网——每日最新资讯28at.com

eo028资讯网——每日最新资讯28at.com

所以上面构造函数的写法中虽然y出现在了x的前面,但是在成员变量声明的时候是先声明了x的,所以初始化的时候是先初始化了x,但是把一个未经初始化的y赋值给了x,那肯定是不能成功赋值的, 所以通过getX方法获取到的值也就不是你想要的那个值了。eo028资讯网——每日最新资讯28at.com

3、对于继承而来的派生类的成员初始化顺序是怎么样的呢?

尽管在派生类对象中含有从基类继承而来的成员,但是派生类并不能直接初始化这些成员。和其他创建了基类对象的代码一样,派生类也必须使用基类的构造函数来初始化它的基类部分。 首先初始化基类的部分,然后按照声明的顺序依次初始化派生类的成员。eo028资讯网——每日最新资讯28at.com

eo028资讯网——每日最新资讯28at.com

3、委托构造函数的执行顺序

所谓委托构造函数就是构造函数相互调用。eo028资讯网——每日最新资讯28at.com

当一个构造函数委托给另一个构造函数时,受委托的构造函数的初始值列表和函数体被依次执行。 如果受委托的构造函数体恰好是空的。假如函数体包含有代码的话,将先执行这些代码,然后控制权才会交还给委托者的函数体。eo028资讯网——每日最新资讯28at.com

eo028资讯网——每日最新资讯28at.com

4、构造函数异常如何捕获

处理构造函数初始值异常的唯一方法是将构造函数写成函数try语句块。eo028资讯网——每日最新资讯28at.com

5、如何让类不能在栈内构造

笔者查了下网上的资料说大概就是说将构造方法私有化,并且将拷贝构造函数私有化就能禁止类的对象在栈内构造了。笔者测试了一下其实这并不严谨,这样的做法只能做到在类的外部禁用了栈内初始化, 在类的内部依然可以使用栈的方式构造对象,比如一下例子:eo028资讯网——每日最新资讯28at.com

class Data {public:// 在类的内部依然可以使用栈的方式构造    Data create() {        Data data = Data();    }private:    Data();    Data(const Data &data) {    }};

经过笔者的测试,私有化构造函数,再加上使用delete关键字移除拷贝构造函数即可实现禁用类在栈内构造的功能:eo028资讯网——每日最新资讯28at.com

class Data {public:    // 不能在栈内构造,编译会报错    Data create() {        return Data();    }private:    Data();    Data(const Data &data) = delete;};

但是这种做法实在是太过了,而且笔者笔者才疏学浅,也不知道这种做法会不会造成什么隐藏的坑,如有高手,请赐教。eo028资讯网——每日最新资讯28at.com

《More EffectiveC++》一书中第27条:要求(或禁止)对象产生与heap之中,提到将构造函数和析构函数私有化即可达到禁止对象在栈内定义的目的。 但是这个做法太过了,比较好的办法是让析构函数r成为 private,而构造函数仍为 public。eo028资讯网——每日最新资讯28at.com

eo028资讯网——每日最新资讯28at.com

6、如何让类不能在堆内构造对象

使用new在堆内构造对象主要会调用构造函数以及new运算符这两个步骤,所以我们只要把运算符new移除即可:eo028资讯网——每日最新资讯28at.com

class Data {public:    Data();    // 重载new运算符,禁止使用new在堆内构造对象    void* operator new (size_t size) = delete;};

然而笔者发现,虽然这样能够禁用new在堆内构造对象,但是我们知道使用 malloc 也能在堆内分配对象,只是使用 malloc 不会调用类的构造函数而已,所以类内的所有成员都需要自己手动初始化, 那么有没有办法把malloc也禁用掉呢?笔者并不知晓,恳请高手赐教。。。eo028资讯网——每日最新资讯28at.com

在《Effective C++》一书中第06条有提到为驳回编译器自动(暗自)提供的机能,可将相应的成员函数声明为private并且不予实现。eo028资讯网——每日最新资讯28at.com

所以为了达到某个类只能在堆内或者只能在栈内构造的目的可以参考这一条。eo028资讯网——每日最新资讯28at.com

总结一下

  • 谁先声明谁先初始化,与构造函数中出现的顺序无关;
  • 初始化值中的相关调用比构造函数中的函数体优先执行;
  • 在派生类中首先初始化基类的部分,然后按照声明的顺序依次初始化派生类的成员。

本文链接:http://www.28at.com/showinfo-26-34928-0.html理解C++之构造函数

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 孩子喜欢飞机,于是我给她做了一个雷达

下一篇: Go 中切片(Slice)的长度与容量

标签:
  • 热门焦点
  • 从 Pulsar Client 的原理到它的监控面板

    背景前段时间业务团队偶尔会碰到一些 Pulsar 使用的问题,比如消息阻塞不消费了、生产者消息发送缓慢等各种问题。虽然我们有个监控页面可以根据 topic 维度查看他的发送状态,
  • 如何正确使用:Has和:Nth-Last-Child

    我们可以用CSS检查,以了解一组元素的数量是否小于或等于一个数字。例如,一个拥有三个或更多子项的grid。你可能会想,为什么需要这样做呢?在某些情况下,一个组件或一个布局可能会
  • 一篇聊聊Go错误封装机制

    %w 是用于错误包装(Error Wrapping)的格式化动词。它是用于 fmt.Errorf 和 fmt.Sprintf 函数中的一个特殊格式化动词,用于将一个错误(或其他可打印的值)包装在一个新的错误中。使
  • 雅柏威士忌多款单品价格大跌,泥煤顶流也不香了?

    来源 | 烈酒商业观察编 | 肖海林今年以来,威士忌市场开始出现了降温迹象,越来越多不断暴涨的网红威士忌也开始悄然回归市场理性。近日,LVMH集团旗下苏格兰威士忌品牌雅柏(Ardbeg
  • 中国家电海外掘金正当时|出海专题

    作者|吴南南编辑|胡展嘉运营|陈佳慧出品|零态LT(ID:LingTai_LT)2023年,出海市场战况空前,中国创业者在海外纷纷摩拳擦掌,以期能够把中国的商业模式、创业理念、战略打法输出海外,他们依
  • 腾讯盖楼,字节拆墙

    来源 | 光子星球撰文 | 吴坤谚编辑 | 吴先之“想重温暴刷深渊、30+技能搭配暴搓到爽的游戏体验吗?一起上晶核,即刻暴打!”曾凭借直播腾讯旗下代理格斗游戏《DNF》一
  • 重估百度丨大模型,能撑起百度的“今天”吗?

    自象限原创 作者|程心 罗辑2023年之前,对于自己的“今天”,百度也很迷茫。“新业务到 2022 年底还是 0,希望 2023 年出来一个 1。”这是2022年底,李彦宏
  • 华为开发者大会2023日程公开:开设鸿蒙HarmonyOS 4体验区

    IT之家 7 月 31 日消息,华为今日公布了 HDC.Together 开发者大会 2023 的详细日程。整场大会将于 8 月 4 日-6 日之间举行,届时将发布最新一代鸿蒙 H
  • 国行版三星Galaxy Z Fold5/Z Flip5发布 售价7499元起

    2023年8月3日,三星电子举行Galaxy新品中国发布会,正式在国内推出了新一代折叠屏智能手机三星Galaxy Z Fold5与Galaxy Z Flip5,以及三星Galaxy Tab S9
Top