在C++面向对象编程中,类中定义常量以及正确使用成员初始化列表是开发者需要掌握的基础技能,这两部分内容直接关系到类的设计合理性和代码的正确性。

C++类中定义常量的方法
1. 使用const修饰成员变量
在类中用const修饰的成员变量属于类常量,这类常量必须在构造函数的初始化列表中完成初始化,不能在构造函数体内赋值。因为const变量一旦定义就需要初始化,而构造函数体内执行的已经是赋值操作,不符合const变量的初始化要求。
示例代码如下:
#include <iostream>
#include <string>
class Student {
private:
const int max_score; // const修饰的类常量
std::string name;
public:
// 必须在初始化列表中初始化const成员
Student(const std::string& stu_name, int score) : max_score(score), name(stu_name) {
// 构造函数体内不能对max_score赋值,下面这行会报错
// max_score = 100;
}
void print_info() {
std::cout << "学生姓名:" << name << ",最高分:" << max_score << std::endl;
}
};
int main() {
Student stu("张三", 100);
stu.print_info();
return 0;
}
2. 使用static const修饰静态常量
如果类中的常量是所有对象共享的,可以使用static const修饰。对于整型或枚举类型的static const成员,可以在类内直接初始化;非整型的static const成员通常需要在类外单独定义初始化。
示例代码如下:
#include <iostream>
#include <string>
class School {
public:
// 整型静态常量可以在类内直接初始化
static const int total_grade = 6;
// 非整型静态常量需要在类外初始化
static const std::string school_name;
};
// 类外初始化静态常量
const std::string School::school_name = "阳光小学";
int main() {
std::cout << "学校名称:" << School::school_name << ",总年级数:" << School::total_grade << std::endl;
return 0;
}
3. 使用枚举类型定义类内常量
还可以用枚举类型在类中定义常量,枚举值默认是整型,属于类内作用域,所有对象共享。这种方式适合定义一组相关的常量。
示例代码如下:
#include <iostream>
class ErrorCode {
public:
enum {
SUCCESS = 0,
PARAM_ERROR = 1,
NETWORK_ERROR = 2
};
};
int main() {
std::cout << "成功状态码:" << ErrorCode::SUCCESS << std::endl;
std::cout << "参数错误状态码:" << ErrorCode::PARAM_ERROR << std::endl;
return 0;
}
C++类成员初始化列表使用规范
基本语法规范
成员初始化列表位于构造函数参数列表之后,函数体之前,以冒号开头,多个成员初始化用逗号分隔,每个成员的初始化形式为成员名(初始值)。初始化列表中的初始化顺序只和成员在类中声明的顺序有关,和初始化列表中的书写顺序无关。
示例说明初始化顺序的代码:
#include <iostream>
class Demo {
private:
int a;
int b;
public:
// 初始化列表顺序是b在前a在后,但实际先初始化a再初始化b,因为a声明在前
Demo(int x) : b(x), a(b) {
// 此时a的值是未定义的b的初始值,属于错误用法
}
void print() {
std::cout << "a=" << a << ", b=" << b << std::endl;
}
};
必须使用初始化列表的场景
- 初始化
const成员变量,如前面提到的const int max_score。 - 初始化引用类型成员变量,引用必须在定义时初始化,所以引用成员必须在初始化列表中完成初始化。
- 初始化没有默认构造函数的类类型成员,如果成员所属的类没有无参构造函数,就必须在初始化列表中调用它的有参构造函数。
对应场景的示例代码:
#include <iostream>
#include <string>
// 没有默认构造函数的类
class Book {
private:
std::string title;
public:
Book(const std::string& t) : title(t) {}
};
class Reader {
private:
const int reader_id; // const成员
int& ref_count; // 引用成员
Book book; // 无默认构造函数的类成员
public:
Reader(int id, int& count, const std::string& book_title)
: reader_id(id), ref_count(count), book(book_title) {
// 三个成员都必须在初始化列表中初始化,不能在函数体内赋值
}
void print() {
std::cout << "读者ID:" << reader_id << ",书籍:" << book_title << std::endl;
}
};
常见错误用法规避
- 不要在初始化列表中使用尚未初始化的成员给其他成员赋值,避免未定义行为。
- 不要对已经在初始化列表中初始化的成员在构造函数体内再次赋值,尤其是
const和引用成员,再次赋值会直接报错。 - 初始化列表中的参数如果和成员变量同名,要明确区分,避免赋值错误。
规避同名参数错误的示例:
#include <iostream>
#include <string>
class Person {
private:
std::string name;
int age;
public:
// 参数和成员同名时,用this指针区分或者参数换名
Person(std::string name, int age) : name(name), age(age) {
// 这里初始化列表的name是参数,赋值给成员name,是正确的
// 如果写成name = name,只是给参数自己赋值,成员不会被初始化
}
void print() {
std::cout << "姓名:" << name << ",年龄:" << age << std::endl;
}
};
总结
在C++类中定义常量可以根据场景选择const成员、static const成员或者枚举类型,不同方式的适用范围和初始化要求不同。类成员初始化列表是构造函数初始化成员的正确方式,尤其对于const成员、引用成员和无默认构造函数的类成员是必须使用的。开发者需要遵循初始化列表的语法规范,注意成员的初始化顺序,规避常见的错误用法,才能写出符合规范、逻辑正确的C++类代码。