一、入门
1、C++中基础类型的初始化方式有哪些?请举例说明
默认初始化
对于全局变量和静态变量,基础类型(如int
、float
、double
等)会被初始化为 0;而对于局部变量,其值是未定义的,包含随机的垃圾值。
全局变量初始化为0,局部变量值未定义
#include <iostream>
int global_x; // 全局变量,默认初始化为 0
void testDefaultInitialization() {
int local_x; // 局部变量,值未定义
std::cout << "Global x: " << global_x << std::endl;
std::cout << "Local x: " << local_x << std::endl; // 输出值不确定
}
拷贝初始化—— 有赋值符
int y = 5;
(通过等号赋值初始化,支持隐式类型转换)
int main() {
int y = 5; // 常规拷贝初始化
double d = 3.14;
int z = d; // 隐式类型转换,d 从 double 转为 int,小数部分被截断
std::cout << "y: " << y << ", z: " << z << std::endl;
return 0;
}
隐式转换陷阱:若目标类型存在单参数构造函数,可能触发隐式类型转换(如MyClass obj = 10
会调用MyClass(int)
构造函数)
直接初始化 —— ()构造函数
int z(6);
或std::vector<int> v(10, 5); 生成10个5,
调用vector
的构造函数vector(size_type count, const T& value)
实现的- 当编译器遇到
vector<int> v(10)
时,会优先调用vector(size_type count)
,生成 10 个 0 - vector<int> v{10}; // 实际调用 initializer_list 构造函数,生成 1 个元素 10
初始化方式 | 预期行为 | 实际可能行为(未正确处理时) |
---|---|---|
vector<int> v(10, 5); | 生成 10 个 int 类型的 5 | 正常调用数值型构造函数 |
vector<int> v(10, 5.0) | 预期同上5.0 转 int | 若匹配到模板构造函数,导致 编译错误 或 非法内存访问 |
显式调用构造函数,避免隐式转换
#include <iostream>
class MyInt {
public:
MyInt(int value) : data(value) {}
int getData() const { return data; }
private:
int data;
};
int main() {
MyInt obj(6); // 直接初始化,显式调用构造函数
std::cout << "Object data: " << obj.getData() << std::endl;
return 0;
}
列表初始化(C++11)
int a{7};
或 int b = {8};
(统一语法,禁止窄化转换)
#include <iostream>
int main() {
int a{7}; // 列表初始化
int b = {8}; // 另一种列表初始化形式
// int c{3.14}; // 错误,禁止窄化转换
std::cout << "a: " << a << ", b: " << b << std::endl;
return 0;
}
- 统一初始化语法,适用于基本类型、数组、容器(如
std::map<int, std::string> m{{1, "one"}, {2, "two"}}
) - 优先匹配
std::initializer_list
构造函数(如vector<int>{3,5}
生成[3,5]
而非[5,5,5]
)
2、类对象的默认构造函数何时会被调用?
当对象声明不带参数时自动调用:MyClass obj;——
默认构造函数
class MyClass {
public:
MyClass() {
std::cout << "Default constructor called" << std::endl;
}
};
int main() {
MyClass obj; // 自动调用默认构造函数
return 0;
}
STL 容器(如 vector<MyClass>
)或数组(如 MyClass arr[5]
)要求元素类型必须具有默认构造函数,否则无法初始化
#include <iostream>
#include <vector>
class MyClass {
public:
MyClass() {
std::cout << "Default constructor called" << std::endl;
}
};
int main() {
std::vector<MyClass> vec(3); // 调用 3 次默认构造函数
MyClass arr[2]; // 调用 2 次默认构造函数
return 0;
}
std::vector<MyClass> vec(10); // 错误:若 MyClass 无默认构造函数
// 没有默认构造函数的时候
MyClass obj; // 错误:找不到默认构造函数
若基类未显式定义默认构造函数,派生类的默认构造函数会因无法调用基类默认构造函数而报错
#include <iostream>
class Base {
public:
Base(int x) {
std::cout << "Base constructor called with x: " << x << std::endl;
}
};
class Derived : public Base {
public:
// 错误:Derived 的默认构造无法调用 Base 的默认构造
// Derived() {}
};
int main() {
// Derived obj; // 编译错误
return 0;
}
作为类的成员变量初始化:如果一个类包含另一个类类型的成员变量,并且在该类的构造函数中没有显式初始化这个成员变量,那么会调用该成员变量类型的默认构造函数。
class InnerClass { public: InnerClass() { std::cout << "InnerClass default constructor called" << std::endl; } }; class OuterClass { public: InnerClass inner; OuterClass() { std::cout << "OuterClass constructor called" << std::endl; } }; int main() { OuterClass obj; return 0; }
二、进阶
1、类的成员变量有哪些初始化方式?优先级如何?
初始化方式 | 执行阶段 | 优先级 | 示例 |
---|---|---|---|
类内初始值 | 编译期 | 最低 | int m_val = 10; |
初始化列表 | 构造函数体执行前 | 最高 | MyClass(int x) : m_val(x) {} |
构造函数体内赋值 | 构造函数体执行时 | 中等 | MyClass(int x) { m_val = x; } |
- 类内初始值(C++11)
int m_val = 10;
(声明时直接赋值,优先级最低)
class MyClass {
public:
int m_val = 10; // 类内初始值
MyClass() {
std::cout << "m_val in constructor: " << m_val << std::endl;
}
};
int main() {
MyClass obj;
return 0;
}
- 构造函数初始化列表
MyClass(int x) : m_val(x) {}
(优先级最高,先于构造函数体执行)
class MyClass {
public:
int m_val;
MyClass(int x) : m_val(x) {
std::cout << "m_val in constructor: " << m_val << std::endl;
}
};
int main() {
MyClass obj(20);
return 0;
}
- 构造函数体内赋值
MyClass(int x) { m_val = x; }
(会覆盖前两种初始化)
class MyClass {
public:
int m_val = 10; // 类内初始值
MyClass(int x) {
m_val = x; // 构造函数体内赋值
std::cout << "m_val in constructor: " << m_val << std::endl;
}
};
int main() {
MyClass obj(30);
return 0;
}
- 初始化顺序:按成员声明顺序执行,与初始化列表顺序无关(如
A() : y(10), x(y)
可能导致x
使用未初始化的y
) - 性能对比:初始化列表直接构造,而构造函数体内赋值需先默认构造再赋值(对复杂对象有性能差异)
2、静态成员变量的初始化方式是怎样的?
静态成员变量属于类而不是类的某个对象,需要在类外进行初始化。
class MyClass {
public:
static int s_val;
};
int MyClass::s_val = 10; // 类外初始化
如果静态成员变量是常量整数类型(如const int
),可以在类内进行初始化。
class MyClass {
public:
static const int s_const_val = 20;
};
三、高阶
1、列表初始化的底层原理是什么?何时会触发std::initializer_list
构造函数?
编译器将{1,2,3}
转换为临时数组const int[3]
,并生成std::initializer_list
对象(包含数组指针和大小)
触发条件:
- 类定义了
std::initializer_list
参数的构造函数 - 优先匹配规则:当存在
initializer_list
构造函数时,{}
语法会强制优先调用该版本
vector<int> v1(3,5); // 生成[5,5,5]
vector<int> v2{3,5}; // 生成[3,5],优先调用initializer_list版本
在模板编程中,列表初始化可以让模板函数或类更灵活地处理不同类型的初始化列表。另外,当使用列表初始化创建对象时,如果类没有合适的std::initializer_list
构造函数,但有其他可以接受相应参数的构造函数,编译器会尝试进行普通的构造函数匹配。
template<typename T>
void printList(const std::initializer_list<T>& list) {
for (const auto& element : list) {
std::cout << element << " ";
}
std::cout << std::endl;
}
int main() {
printList({1, 2, 3, 4});
printList({'a', 'b', 'c'});
return 0;
}
2、C++17对枚举类型的初始化有何优化?请举例说明
C++17允许直接通过整型值列表初始化枚举类型,无需显式转换
enum class Month : int { Jan=1, Dec=12 };
Month m{12}; // C++17合法,等同于static_cast<Month>(12)