非静态数据成员初始化(NSDMI)是C++11标准引入的重要特性,指的是在类的定义内部直接为非静态数据成员指定初始值的能力,也被叫做类内初始化。在C++11之前,非静态数据成员只能在构造函数的初始化列表或者构造函数体内完成初始化,类定义中不能直接给成员赋值,这个限制让很多类的初始化代码变得冗余。

NSDMI的基本语法
使用NSDMI的语法非常简单,只需要在类定义中声明非静态数据成员时,在成员后面加上等号或者花括号初始化器即可,不需要额外的关键字。需要注意的是,只有非静态数据成员可以使用这个特性,静态数据成员依然需要遵循之前的初始化规则。
下面是基础的语法示例:
#include <iostream>
#include <string>
class User {
public:
// 非静态数据成员类内初始化,使用等号初始化
int id = 1001;
// 使用花括号初始化
std::string name{"默认用户"};
// 内置类型也可以用花括号初始化
double score{0.0};
};
int main() {
User u;
std::cout << "id: " << u.id << std::endl;
std::cout << "name: " << u.name << std::endl;
std::cout << "score: " << u.score << std::endl;
return 0;
}
初始化优先级规则
当类中同时存在NSDMI和构造函数的初始化列表时,会遵循固定的优先级规则:构造函数的初始化列表优先级高于类内初始化。也就是说,如果构造函数的初始化列表对该成员进行了初始化,那么类内指定的初始值会被忽略,直接使用初始化列表的值;如果构造函数的初始化列表没有覆盖该成员,才会使用类内初始值。
我们可以通过下面的代码验证这个规则:
#include <iostream>
#include <string>
class Product {
public:
// 类内初始值
std::string name{"未知商品"};
double price = 0.0;
int stock{10};
// 构造函数1:初始化列表覆盖部分成员
Product(const std::string& n, double p) : name(n), price(p) {
// 这里stock没有在初始化列表中出现,会使用类内的初始值10
}
// 构造函数2:初始化列表覆盖所有成员
Product(const std::string& n, double p, int s) : name(n), price(p), stock(s) {
// 所有成员都使用初始化列表的值,类内初始值被忽略
}
};
int main() {
Product p1("手机", 2999.0);
std::cout << "p1 name: " << p1.name << ", price: " << p1.price << ", stock: " << p1.stock << std::endl;
Product p2("电脑", 5999.0, 20);
std::cout << "p2 name: " << p2.name << ", price: " << p2.price << ", stock: " << p2.stock << std::endl;
return 0;
}
NSDMI的使用限制
虽然NSDMI带来了很多便利,但是也有一些使用限制需要开发者注意:
- 只能用于非静态数据成员,静态数据成员不能使用类内初始化,依然需要在类外定义并初始化。
- 类内初始化的表达式不能是
this指针,因为成员初始化时对象还没有完全构造完成,this指针还不可用。 - 初始值不能是正在构造的类的对象,比如类A的成员不能用A的实例来初始化,会导致递归初始化问题。
- 对于数组类型的非静态成员,类内初始化只能使用花括号初始化器,不能使用等号初始化。
NSDMI的优势
相比C++11之前的方式,NSDMI主要有几个明显的优势:
减少重复代码:如果多个构造函数都需要对同一个成员设置相同的初始值,之前需要在每个构造函数的初始化列表都写一遍,现在只需要在类内写一次即可。
避免遗漏初始化:如果新增了非静态数据成员,忘记在构造函数初始化列表中添加,就会导致成员未初始化,使用NSDMI可以给成员一个默认的合理初始值,降低出错概率。
提升代码可读性:成员初始值和成员声明放在一起,开发者看类定义的时候就能直接知道成员的默认取值,不需要跳转到构造函数查看初始化逻辑。
常见使用场景
NSDMI适合用在以下场景:
- 类的非静态数据成员有通用的默认取值,大部分情况下不需要修改。
- 类有多个构造函数,多个构造函数都需要对同一个成员设置相同的初始值。
- 需要给成员一个安全的默认初始值,避免未初始化导致的未定义行为。
需要注意的是,如果成员的初始值依赖构造函数的参数,那么还是需要在构造函数的初始化列表中完成初始化,NSDMI更适合设置固定的默认初始值。
C++11非静态数据成员初始化NSDMI类内初始化修改时间:2026-07-04 00:21:30