当前位置:首页 > C++系列 > C++实例技巧 > 正文内容

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

3周前 (11-05)C++实例技巧112

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

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

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

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, 82.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);
    stu1.show();
    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);
    stu1.show();
    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);
    stu1.show();
    return 0;
}

image.png

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

    扫描二维码推送至手机访问。

    版权声明:本文由周伯通的博客发布,如需转载请注明出处。

    本文链接:http://zhoubotong.site/post/87.html

    分享给朋友:
    返回列表

    上一篇:C++ Null 指针的使用

    没有最新的文章了...

    相关文章

    C++ Null 指针的使用

    C++ Null 指针的使用

    这里有必要说下关于空指针的使用注意事项, C++ 中,如果一个指针不指向任何数据,就称之为空指针,用 NULL 表示。注意,NULL 是区分大小写的,即 NULL 不能写成 null。C++ NULL...

    发表评论

    访客

    看不清,换一张

    ◎欢迎参与讨论,请在这里发表您的看法和观点。