隐式转换是C++编译器自动进行的类型转换,而explicit关键字可以防止不期望的隐式转换。理解这两个概念对于编写安全的C++代码非常重要。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
#include <iostream>
#include <string>
using namespace std::string_literals;
class Entity
{
private:
std::string m_Name;
int m_Age;
public:
// 单参数构造函数可以用于隐式转换
Entity(const std::string& name)
: m_Name(name), m_Age(-1) {}
Entity(int age)
: m_Name("Unknown"), m_Age(age) {}
const std::string& GetName() const { return m_Name; }
int GetAge() const { return m_Age; }
};
void PrintEntity(const Entity& entity)
{
std::cout << "Name: " << entity.GetName()
<< ", Age: " << entity.GetAge() << std::endl;
}
int main()
{
// 直接构造
Entity a("Jerry");
Entity b(22);
// 隐式转换 - 编译器自动调用构造函数
Entity c = "Alice"s; // 等价于 Entity c("Alice")
Entity d = 25; // 等价于 Entity d(25)
// 函数参数的隐式转换
PrintEntity("Bob"s); // 隐式转换为Entity
PrintEntity(30); // 隐式转换为Entity
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
class SafeEntity
{
private:
std::string m_Name;
int m_Age;
public:
SafeEntity(const std::string& name)
: m_Name(name), m_Age(-1) {}
// 使用explicit防止隐式转换
explicit SafeEntity(int age)
: m_Name("Unknown"), m_Age(age) {}
const std::string& GetName() const { return m_Name; }
int GetAge() const { return m_Age; }
};
int main()
{
SafeEntity a("Jerry"); // 正确:直接构造
SafeEntity b(22); // 正确:直接构造
SafeEntity c = "Alice"s; // 正确:string构造函数没有explicit
// SafeEntity d = 25; // 错误:int构造函数是explicit的
SafeEntity d(25); // 正确:显式构造
PrintEntity("Bob"s); // 正确:string构造函数可以隐式转换
// PrintEntity(30); // 错误:int构造函数是explicit的
PrintEntity(SafeEntity(30)); // 正确:显式构造
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class Distance
{
private:
double m_Meters;
public:
Distance(double meters) : m_Meters(meters) {}
double GetMeters() const { return m_Meters; }
};
void ProcessDistance(const Distance& dist)
{
std::cout << "Distance: " << dist.GetMeters() << " meters" << std::endl;
}
int main()
{
ProcessDistance(100.0); // 意图:100米
ProcessDistance(3.14); // 意图:3.14米
ProcessDistance(42); // 可能是错误:本意可能是42厘米?
// 更危险的情况
bool isReady = true;
ProcessDistance(isReady); // bool隐式转换为1.0,可能不是期望的
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
class SafeDistance
{
private:
double m_Meters;
public:
explicit SafeDistance(double meters) : m_Meters(meters) {}
double GetMeters() const { return m_Meters; }
};
void ProcessSafeDistance(const SafeDistance& dist)
{
std::cout << "Distance: " << dist.GetMeters() << " meters" << std::endl;
}
int main()
{
ProcessSafeDistance(SafeDistance(100.0)); // 必须显式构造
ProcessSafeDistance(SafeDistance(3.14)); // 必须显式构造
// ProcessSafeDistance(42); // 编译错误:防止意外转换
// ProcessSafeDistance(true); // 编译错误:防止意外转换
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
class Point
{
private:
double m_X, m_Y;
public:
// 多参数构造函数也可以使用explicit
explicit Point(double x, double y) : m_X(x), m_Y(y) {}
// 单参数构造函数
explicit Point(double value) : m_X(value), m_Y(value) {}
double GetX() const { return m_X; }
double GetY() const { return m_Y; }
};
int main()
{
Point p1(1.0, 2.0); // 正确:直接构造
Point p2{3.0, 4.0}; // 正确:列表初始化
// Point p3 = {5.0, 6.0}; // 错误:explicit阻止列表初始化的隐式转换
Point p3{5.0, 6.0}; // 正确:直接列表初始化
Point p4(7.0); // 正确:单参数构造
// Point p5 = 8.0; // 错误:explicit阻止隐式转换
}
|
除了构造函数,还可以定义转换操作符:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
class Temperature
{
private:
double m_Celsius;
public:
explicit Temperature(double celsius) : m_Celsius(celsius) {}
// 隐式转换操作符
operator double() const { return m_Celsius; }
// explicit转换操作符
explicit operator bool() const { return m_Celsius > 0; }
double GetCelsius() const { return m_Celsius; }
};
int main()
{
Temperature temp(25.0);
// 隐式转换为double
double value = temp; // 调用operator double()
std::cout << value << std::endl;
// explicit转换为bool
// bool isPositive = temp; // 错误:explicit转换操作符
bool isPositive = static_cast<bool>(temp); // 正确:显式转换
if (temp) // 正确:在条件语句中可以隐式转换
{
std::cout << "Temperature is positive" << std::endl;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class GoodPractice
{
public:
// 单参数构造函数通常应该是explicit的
explicit GoodPractice(int value) {}
// 除非隐式转换是有意义且安全的
GoodPractice(const std::string& str) {} // string转换通常是安全的
// 拷贝和移动构造函数不需要explicit
GoodPractice(const GoodPractice& other) {}
GoodPractice(GoodPractice&& other) {}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// C++11之后,可以使用delete禁用特定转换
class ModernClass
{
public:
ModernClass(int value) {}
// 禁用从double的隐式转换
ModernClass(double) = delete;
// 禁用从bool的隐式转换
ModernClass(bool) = delete;
};
int main()
{
ModernClass obj1(42); // 正确
// ModernClass obj2(3.14); // 错误:被delete
// ModernClass obj3(true); // 错误:被delete
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
class SmartPointer
{
private:
int* m_Ptr;
public:
explicit SmartPointer(int* ptr) : m_Ptr(ptr) {}
~SmartPointer() { delete m_Ptr; }
// 禁用拷贝
SmartPointer(const SmartPointer&) = delete;
SmartPointer& operator=(const SmartPointer&) = delete;
// 转换为bool,检查是否为空
explicit operator bool() const { return m_Ptr != nullptr; }
int& operator*() const { return *m_Ptr; }
int* operator->() const { return m_Ptr; }
};
int main()
{
SmartPointer ptr(new int(42));
if (ptr) // 使用explicit operator bool
{
std::cout << *ptr << std::endl;
}
// SmartPointer ptr2 = new int(100); // 错误:explicit构造函数
SmartPointer ptr2(new int(100)); // 正确:显式构造
}
|
- 隐式转换:只要有对应参数类型的构造函数存在,就可以被隐式转换,这种称为隐式构造函数,但这可能导致不期望的行为
- explicit关键字:防止隐式转换,提高代码安全性
- 最佳实践:单参数构造函数通常应该使用explicit,除非隐式转换是有意义且安全的
- 现代C++:可以使用delete关键字禁用特定的转换