摘要:在C++11之后,声明时初始化->初始化列表->构造函数初始化。

本文分享自华为云社区《如何编写高效、优雅、可信代码系列(3)——类成员初始化的三种方式》,原文作者:我是一颗大西瓜。

首先,先得了解一下C++支持哪几种类成员初始化的方式,你常用的又是哪一种。

  • 初始化方式一:初始化列表
class A
{
public:
int a; // 初始化列表
A(int a_):a(a_){}
};
  • 初始化方式二:构造函数初始化
class A
{
public:
int a; // 初始化列表
A(int a_, bool b) { a = a_; }
};
  • 初始化方式三:声明时初始化(也称就地初始化,c++11后支持)
class A
{
public:
int a = 1; // 声明时初始化
A() {}
};

在C++98中,支持了在类声明中使用等号“=”加初始值的方式,来初始化类中静态成员常量。这种声明方式我们也称之为“就地”声明。就地声明在代码编写时非常便利,不过C++98对类中就地声明的要求却非常高。如果静态成员不满足常量性,则不可以就地声明,而且即使常量的静态成员也只能是整型或者枚举型才能就地初始化。而非静态成员变量的初始化则必须在构造函数中进行。比如,如下代码在c++98中编译

class Init
{
public:
Init(): a(0) []
Init(int d): a(d) {}
private:
int a;
const static int b = 0; int c = 1; // member, cannot pass build
static int d = 0; // member, cannot pass build static const double e = 1.3; // not int or enum type, cannot pass build
stati const char* const f = "e"; // not int or enum type, cannot pass build
}

这非常不方便,所以在C++11中,标准允许非静态成员变量的初始化有多种形式。具体而言,除了初始化列表外,在C++11中,标准还允许使用等号= 或者 花括号{} 进行就地的非静态成员变量初始化。

struct init {
int a = 1;
double b {1.2};
};

大家知道,有几种情况下推荐优先使用列表初始化

  • const成员变量只能用成员初始化列表来完成初始化,而不能在构造函数内赋值
  • 初始化的数据成员是对象
  • 需要初始化引用成员数据

具体的原因这里不细述,大家可以去看一下《C++ Primer》。

构造函数初始化的本质是赋值操作("="),这个方法存在两个问题,一个是比起初始化列表和就地初始化,此方式的效率偏低;第二个是可能存在错误隐患。

先说第一个,赋值过程中会产生临时对象,临时对象的构造析构会造成效率损耗,初始化列表的方式就避免了产生临时对象缩带来的问题。

第二个是,如果你没有重写或者禁止赋值构造函数,c++会悄悄的加上默认的赋值构造函数,这个时候也有可能带来问题。

从C++11之后,这三种初始化的方法都可以使用,并不会存在冲突,但是,他们之间是有优先级顺序的,这个优先级来源于他们在初始化的时间顺序,后面初始化的会把前面的覆盖掉,成员变量的初始化顺序是

声明时初始化->初始化列表->构造函数初始化

因此假如三种初始化方式同时存在的话,那么最后保留的成员变量值肯定是构造函数中初始化的值。

#include <iostream>
using namespace std;
class A
{
public:
int a = 1;
A(int a_) :a(2) { a = 3; }
}; int main()
{
A a;
cout << "a.a=" << a.a << endl;
return 0;
} // a.a=3

既然初始化方式这么多,那么什么时候适用哪种呢?

1. 声明时初始化的使用场景

  • 一个优点是直观,你在声明的时候顺便给一个初始值,bravo,别人在看你代码的时候,点一下调到声明也能看到你赋予的初始值,不用再去看构造函数那里给的什么值
  • 第二个优点更有用了,比如你要定义多个构造函数,每个构造函数都用列表初始化的方法初始化,多麻烦呀,请看下面的例子,妈妈看了再也不用担心我想用其他初始化方法了
class Group {
public:
Group() {}
Group(int a): data(a) {}
Group(Mem m): mem(m) {}
Group(int a, Mem m, string n): data(a), mem(m), name(n) {}
private:
int data = 1;
Mem mem{0};
string name{"Group"};
};

2. 列表初始化的使用场景

前面说过了三个场景,这里赘述一下

  • const成员变量只能用成员初始化列表来完成初始化,而不能在构造函数内赋值
  • 初始化的数据成员是对象
  • 需要初始化引用成员数据

但是,需要注意列表初始化的顺序,不过IDE会提示你的

3. 构造函数初始化的使用场景

  • 第一个就是拷贝和赋值构造函数里(不然怎么叫赋值构造函数呢)
  • 第二个就是比较无聊的情况了,比如你想把几个成员函数都初始化成一个值,请看下面例子
class Group {
public:
Group() {data1 = data2 = data3 = 0;}
private:
int data1;
int data2;
int data3;
};

一言以蔽之,优先就地初始化和列表初始化。

点击关注,第一时间了解华为云新鲜技术~

带你掌握C++中三种类成员初始化方式的更多相关文章

  1. 深入浅出spring IOC中三种依赖注入方式

    深入浅出spring IOC中三种依赖注入方式 spring的核心思想是IOC和AOP,IOC-控制反转,是一个重要的面向对象编程的法则来消减计算机程序的耦合问题,控制反转一般分为两种类型,依赖注入和 ...

  2. Java中final变量的初始化方式

    原文转自:http://blog.csdn.net/zhangjk1993/article/details/24196847 public class FinalTest1 { //--------- ...

  3. C基础--结构体成员初始化方式

    之前在linux内核代码中看到结构体成员成员初始化使用类似于.owner = THIS_MODULE, 不太见过,于是搜了个博客,分享下: 转自:http://www.cnblogs.com/Anke ...

  4. 【转】Spring 中三种Bean配置方式比较

    今天被问到Spring中Bean的配置方式,很尴尬,只想到了基于XML的配置方式,其他的一时想不起来了,看来Spring的内容还没有完全梳理清楚,见到一篇不错的文章,就先转过来了. 以前Java框架基 ...

  5. Spring 中三种Bean配置方式比较

    今天被问到Spring中Bean的配置方式,很尴尬,只想到了基于XML的配置方式,其他的一时想不起来了,看来Spring的内容还没有完全梳理清楚,见到一篇不错的文章,就先转过来了. 以前Java框架基 ...

  6. JS中三种字符串连接方式及其性能比较

    工作中经常会碰到要把2个或多个字符串连接成一个字符串的问题,在JS中处理这类问题一般有三种方法,这里将它们一一列出顺便也对它们的性能做个具体的比较. 第一种方法  用连接符“+”把要连接的字符串连起来 ...

  7. 【SSH系列】深入浅出spring IOC中三种依赖注入方式

    spring的核心思想是IOC和AOP,IOC-控制反转,是一个重要的面向对象编程的法则来消减计算机程序的耦合问题,控制反转一般分为两种类型,依赖注入和依赖查找,依赖什么?为什么需要依赖?注入什么?控 ...

  8. spring IOC中三种依赖注入方式

    Spring的核心思想是IOC和AOP,IOC-控制反转,是一个重要的面向对象编程的法则,用来消减计算机程序之间的耦合问题,控制反转一般分为两种类型,依赖注入和依赖查找,依赖什么?为什么需要依赖?注入 ...

  9. c++中三种参数引用方式

    传值调用 是默认的参数传递机制,实参会复制给形参,调用的语义是每次取得实参的副本并将该复本用作形参,即会有复本的开销,并且不改变实参的值. 适用于:传值调用用于不应该被函数改变的小型对象. 例子:vo ...

随机推荐

  1. babylin使用思路

  2. Canal详细入门实战(使用总结)

    Canal介绍 Canal简介 canal [kə'næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费 早期阿里巴巴因为杭州和美国双机房部署,存在 ...

  3. 【BUAA软工】Beta阶段事后分析

    设想与目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 解决的问题 总体解决的问题:新手编程者配置编程环境难.本地编写的代码跨设备同步难.本地ide安装使用过程 ...

  4. C# 搞桌面UI适配国产麒麟Linux+龙芯遇到的一些坑

    由于一些国企有国产化的需求,所以搞了C#适配银河麒麟,适配了X64和龙芯MIPS版本 1. 在银河麒麟的龙芯版本中 pipe2 不能使用,x64版本上却可以用.  pipe2 用来做自定义消息的,搞U ...

  5. 密码学系列之:memory-hard函数

    密码学系列之:memory-hard函数 目录 简介 为什么需要MHF Memory hard的评估方法 MHF的种类 MHF的密码学意义 memory-hard在MHF中的应用 简介 Memory ...

  6. [项目] 淘淘商城 Part.2

    商品列表查询 Easyui 商品添加 商品类目选择 图片上传 富文本编辑器使用 添加的实现 展示首页 略 分页插件 在SqlMapConfig.xml,配置一个plugin 在sql语句执行之前,添加 ...

  7. [Qt]《开发指南》3.1源码分析

    界面: ButterflyGraph: 可以看出,本工程在主程序main里调用窗口界面qmywidget,窗口界面继承了QWidget,并调用自定义类qperson,推测是qmywidget类中的一个 ...

  8. KVM 添加新硬件

    1 显卡 spice 2视频 qxl驱动 3 声音 ich6最好  ich9最清楚 4 输入 鼠标 智能图   否则不能VNC找不到焦点 5 磁盘大小 至少80G 否则 无法自动安装  无swap和 ...

  9. CentOS6.7系统文本安装-2020

    CentOS6.7系统文本安装 [日期:2016-01-30] 来源:Linux社区  作者:endmoon [字体:大 中 小]   一.选择虚拟机软件 1)VMware Workstation   ...

  10. Docker —— 使用 Dockerfile 制作 Jdk + Tomcat 镜像

    一.准备好Jdk和Tomcat apache-tomcat-8.5.50.tar.gz jdk-8u212-linux-x64.tar.gz 注意: Jdk 和 Tomcat 记得从官网下载,否则制作 ...