首页 > 编程语言 > C++构造函数初始化列表注意的坑

C++构造函数初始化列表注意的坑

时间:2022-11-05 19:13:03浏览次数:63  
标签:初始化 name age C++ 列表 score Student 构造函数

原文链接:https://www.zhoubotong.site/post/87.html
之所以写这篇文章,是觉得里面有些细节如果不注意,很容易出错或踩坑,网上有很多教程对这块的描述部分存在错误。希望下面的介绍能给大家带来帮助。

大家知道当我们需要初始化类中的成员变量时,除了可以直接在构造函数里面进行直接赋值,还可以使用初始化列表的方式来对成员变量进行初始化。

提到这里,顺便说下什么是构造函数初始化列表。

C++初始化列表

语法

Contructor(type1 var1, type2 var2): m_var1(var1), m_var2(var2)
{
}

参数

属性 描述
type1 形参 var1 的类型。
var1 形参 var1。
type2 形参 var2 的类型。
var2 形参 var2。
m_var1 成员变量 m_var1。
m_var2 成员变量 m_var2。

说明

我们使用初始化列表的方式,用形参 var1 初始化了成员变量 m_var1,用形参 var2 初始化了成员变量 m_var2。举个例子:

#include <iostream>
using namespace std;
class Student {
  private:
    char *m_name;
    int m_age;
    float m_score;

  public:
    Student(char *name, int age, float score);
    void show();
};
// 采用初始化列表
Student::Student(char *name, int age, float score)
    : m_name(name), m_age(age), m_score(score) {
    // TODO
}
void Student::show() {
    cout << m_name << " age is " << m_age << "score is " << m_score << endl;
}
int main() {
    Student stu((char *)"鸠摩智", 28, 5);
    stu.show();

    Student *pstu =
        new Student((char *)"慕容复", 27, 86); // 使用指针对象 new 实例化
    pstu->show();
    return 0;
}

image.png
上面的例子定义构造函数时并没有在函数体中对成员变量赋值,而是在函数首部与函数体之间添加了一个冒号:,后面紧跟m_name(name), m_age(age), m_score(score)语句,
可以理解成相当于函数体内部的m_name = name; m_age = age; m_score = score;也是赋值的意思。
注意:使用构造函数初始化列表并没有效率上的优势,仅仅是书写方便,当成员变量较多时这种写法非常简洁。

下面重点说下需要注意的地方:
初始化列表可以用于全部成员变量,也可以只用于部分成员变量。什么意思?比如我们只对 m_name 使用初始化列表,其他成员变量还是=赋值:

// 采用初始化列表
Student::Student(char *name, int age, float score)
    : m_name(name) { // 只用于部分成员变量
    m_age = age;
    m_score = score;
}

输出结果是一样的。 注意!注意!注意!,成员变量的初始化顺序与初始化列表中列出的变量的顺序无关,它只与成员变量在类中声明的顺序有关。
请大家仔细看下面的代码:

#include <iostream>
using namespace std;
class Student {
  private:
    int m_age;
    float m_score;

  public:
    Student(float s);
    void show();
};
Student::Student(float s) : m_score(s), m_age(m_score) {} //注意这里:我们将m_score放在了m_age的前面,看起来是先给m_score赋值,再给m_age赋值(m_age=m_score),其实不是的
void Student::show() { cout << m_age << ", " << m_score << endl; }
int main() {
    Student stu(99);
    stu.show();
    return 0;
}

image.png

结论

所以成员变量的赋值顺序由它们在类中的声明顺序决定,在 上面的Student 类中,我们先声明的 m_age,再声明的 m_score,
给 m_age 赋值时,m_score 还未被初始化,所以输出的m_age的值是默认类型的0;给 m_age 赋值完成后才给m_score 赋值,此时 m_score 的值才是99。
如果大家对上面理解了,我们再看下面的代码,大家可以猜下输出啥?

#include <iostream>
using namespace std;
class Student {
  private:
    int m_age;
    float m_score;

  public:
    Student(float s);
    void show();
};
Student::Student(float s) : m_score(s), m_age(m_score) {
    m_age = m_score;
    m_score = s;
}

void Student::show() { cout << m_age << ", " << m_score << endl; }
int main() {
    Student stu(99);
    stu.show();
    return 0;
}

输出:99, 99,大家请细品!写到最后再提下:构造函数初始化列表还有一个很重要的作用,那就是初始化 const 成员变量。
网上很多教程说初始化 const 成员变量的唯一方法就是使用初始化列表。这是错误的,描述的不够严谨,为什么这样说,我们来看网上很多教程的例子:

#include <iostream>
using namespace std;
class Student {
  public:
    Student(string name, int age) {
        m_name = name;
        m_age = age;
    }
    void show();

  private:
    const string m_name;
    int m_age;
};
void Student::show() { cout << m_name << ", " << m_age << endl; }

int main() {
    Student stu("鸠摩智", 28);
    stu.show();
    Student stu1("慕容复", 27);
    stushow();
    return 0;
}

输出:
image.png
于是网上很多教程说只能使用初始化列表的方式,来进行初始化,现在,按照网上的说法修改程序如下:

#include <iostream>
using namespace std;
class Student {
  public:
    Student(string name, int age) : m_name(name), m_age(age) {}
    void show();

  private:
    const string m_name;
    int m_age;
};
void Student::show() { cout << m_name << ", " << m_age << endl; }

int main() {
    Student stu("鸠摩智", 28);
    stu.show();
    Student stu1("慕容复", 27);
    stushow();
    return 0;
}

image.png

这次在构造函数上面,使用了初始化列表的方式,初始化了 const 成员变量,这次程序没有报错,因此,const 成员变量只可以使用初始化列表的方式进行初始化( 误人)。
这个描述是错误的,并不是 const 成员变量只能使用初始化列表。在构造函数里面初始化赋值是没有任何问题的。
针对上面的问题我们来改下代码。注意这里只是改变了m_name的数据类型为指针型。

#include <iostream>
using namespace std;
class Student {
  public:
    Student(char *name, int age) { // 使用构造方法初始化赋值
        m_name = name;
        m_age = age;
    }
    void show();

  private:
    const char *m_name; // 注意这里是指针变量的常量
    int m_age;
};
void Student::show() { cout << m_name << ", " << m_age << endl; }

int main() {
    Student stu((char *)"鸠摩智", 28);
    stu.show();
    Student stu1((char *)"慕容复", 27);
    stushow();
    return 0;
}

image.png

所以类中对于 const 修饰,既可以使用初始化列表的方式赋值,也可以使用构造函数的方式赋值。

标签:初始化,name,age,C++,列表,score,Student,构造函数
From: https://www.cnblogs.com/phpper/p/16860856.html

相关文章

  • Effective C++ - 条款13 - 以对象管理资源
    直接使用指针管理对象并不安全,因为可能忘记delete指针/delete语句并未按照预期执行而提前被exception中断了程序etcRAII观念:资源取得时机便是初始化时机.例如使用工厂......
  • Windows 下用 MinGW-64 配置 VScode 的 C/C++ 环境
    蒟蒻第一次发博客,轻喷~我在安装VScode的时候可谓历经磨难,所以就萌生出写这篇文章的想法。Windows下用MinGW-64配置VScode的C/C++环境一、下载MinGW-w64并添......
  • 三级指针动态分配/释放内存(C / C++)
    C语言版本#include<stdio.h>#include<stdlib.h>#definehigh2#definerow3#definecol4intmain(){inti,j,k;//p[2][3][4]int......
  • C++生成DLL给C#调用
    1、添加C++动态链接库(DLL)2、添加头文件test.h#ifndefTEST_H#defineTEST_H//添加要在此处预编译的标头#include"framework.h"#endif//TEST_Hextern"C"_......
  • C++——优先级队列(priority_queue)
    其为队列需要包含头文件#include,其与queue的不同之处在于,可以自定义数据的优先级,让优先级高的排在队列的前面,优先出队;优先队列具有队列的所有特性,包括基本操作,只是在此基......
  • 使用nvm配置nodejs,已经nodejs使用的初始化步骤
    <-----------------------------------------nvm是什么------------------------------------------------->nvm:进行node版本切换管理手动配置nvm,需要进入地址https://gi......
  • 初步认识c++之命名空间详解(千字长文带你刨析你命名空间的细节)
    初识c++之命名空间详解前言:c++是在c语言的基础上进一步发展出来的用于弥补c语言缺陷的语言,是c语言的超集,添加了很多新的特性,使其编程更加方便!下面就让我们开始初步认识c++......
  • JavaSE—变量的作用域和初始化
    一、变量的作用域变量被定义为只在某个程序块内或只在方法体内部有效,这种类型的变量通常被称为“局部变量”。局部变量的作用范围有效,只在相应的方法体或程序块内有效,超出......
  • C++《面向对象程序设计课程设计》
    C++《面向对象程序设计课程设计》《面向对象程序设计课程设计》课程说明适用专业:计算机科学与技术课程周数:5周一、根据计算机科学与技术专业人才培养方案制订。(一)课程......
  • C++ 命名类型转换
    目录​​传统艺能......