C++学习:string构造函数的bug

本文探讨了在C++中调用C语言函数时,将char*转换为STL string过程中遇到的问题,并通过一个具体示例展示了可能导致程序崩溃的情况。

c++中,有时需要调用c语言函数,要把char*转换成stl的string,但使用过程,就出现问题。比如下面代码,就会导致程序coredump。不同的版本,可能结果不同。xcode9.x和vs2005所使用的编译器的都出现这个问题。

#include <string>
using namespace std;
const char* get_cstr(void)
{
    return 0;
}
int main(int argc, const char * argv[])
{
    cout<<string( get_cstr() )<<endl;
    return 0;
}


<think>嗯,用户这次问的是explicit构造函数与普通构造函数的区别,结合之前的讨论记录,用户似乎正在系统学习C++的面向对象特性。之前讨论过Qt多线程机制,现在转向更基础的语法细节,可能是项目开发中遇到了隐式转换引发的问题。用户提供的四段引用很有价值:引用[1]明确解释了explicit禁止隐式转换的核心功能;引用[2]展示拷贝构造函数场景,暗示用户可能混淆了普通构造和拷贝构造;引用[3]的代码对比非常直观;引用[4]则强调了工程实践中的注意事项。这些恰好覆盖了技术要点。需要特别注意术语准确性:普通构造函数实指非explicit的单参构造函数,而多参构造函数本身就不存在隐式转换问题。用户引用的test1类示例里test1t1=55这种写法容易误导,其实55在这里被隐式转换成了临时test1对象。深层需求可能是用户想避免类设计中的陷阱。从引用[4]的工程建议看,用户或许正在开发需要严格类型检查的项目,比如数学计算库或通信协议模块,这类场景必须防止意外类型转换。展示方案要突出对比性:先用test0/test1类展示编译期差异,再解析标准委员会建议(如std::vector的explicit设计)。特别注意区分赋值初始化Aa3=a1(调用拷贝构造)与Aa3=42(可能调用隐式转换)的本质不同。相关问题设计应延伸应用场景:隐式转换在智能指针/容器中的影响是进阶重点,用户后续很可能遇到shared_ptr<int>sp=newint这类陷阱。</think> 好的,我们来详细探讨 C++ 中 `explicit` 构造函数与普通构造函数的区别及使用场景,重点聚焦于“单形参构造函数”。 **核心区别:禁止隐式类型转换** 两者的**根本区别**在于 `explicit` 关键字用于禁止由构造函数定义的**单形参隐式类型转换**。普通构造函数允许这种隐式转换,而 `explicit` 构造函数则强制要求显式调用。 --- **一、行为对比 (核心区别)** 1. **普通构造函数 (不带 `explicit`)** * **允许隐式类型转换:** 如果一个构造函数可以接受一个参数,那么这个构造函数定义了一种从该参数类型到其所属类类型的隐式转换规则。 * **代码示例说明:** ```cpp class MyClass { public: MyClass(int val) : value(val) {} // 普通单形参构造函数 int value; }; void doSomething(const MyClass& obj) { // ... 使用 obj.value ... } int main() { MyClass obj1(10); // 显式调用:直接构造 MyClass obj2 = 20; // 隐式转换:编译器用 20 隐式调用 MyClass(20) 构造临时对象,再用它初始化 obj2 (可能调用拷贝构造,也可能优化掉拷贝)[^1][^2][^3] obj1 = 30; // 隐式转换:编译器用 30 隐式调用 MyClass(30) 构造临时对象,再将其赋值给 obj1 doSomething(40); // 隐式转换:编译器用 40 隐式调用 MyClass(40) 构造临时对象,传递给 doSomething } ``` 在上面示例中,整数 `20`, `30`, `40` 都被编译器**隐式地**转换成了 `MyClass` 类型的临时对象。 2. **`explicit` 构造函数 (带 `explicit`)** * **禁止隐式类型转换:** `explicit` 关键字强制要求使用该构造函数的代码必须是显式的。编译器**不会**尝试使用它进行任何隐式类型转换。 * **代码示例说明:** ```cpp class MyClass { public: explicit MyClass(int val) : value(val) {} // explicit 单形参构造函数 int value; }; void doSomething(const MyClass& obj) { // ... 使用 obj.value ... } int main() { MyClass obj1(10); // 显式调用:正确 (直接构造) // MyClass obj2 = 20; // 错误!禁止隐式转换构造。编译报错[^3] // obj1 = 30; // 错误!禁止隐式转换赋值。编译报错 doSomething(MyClass(40)); // 正确:显式构造临时对象传递 doSomething(40); // 错误!禁止隐式转换。编译报错 } ``` 使用 `explicit` 后,所有需要 `MyClass` 类型的地方,如果要从一个 `int` 获取,**必须**显式地调用 `MyClass(int)` 构造函数。任何试图让编译器自动完成 `int` -> `MyClass` 转换的操作都被视为错误。 --- **二、使用场景与优缺点** 1. **普通构造函数的适用场景(允许隐式转换)** * **期望类型自然、无缝转换:** 当你设计一个类,并且希望从构造函数的参数类型到该类类型的转换应该是自然发生的、符合直觉的(比如 `std::string` 接受 `const char*`)。 * **数值包装类:** 设计一个类来包装基础数值类型(如自定义的 `Meter`、`Kilogram` 单位类),你可能*希望*允许整数隐式转换为该单位类型。 **(但需慎重,可能引入歧义)** * **方便性优先:** 当便利性比严格的类型安全更重要,且你确信隐式转换不会造成歧义或难以发现的错误时。 * **缺点:** 可能导致难以发现的 Bug 和代码歧义。编译器在“默默”地进行类型转换,这种转换有时并非程序员本意,使代码行为不够清晰,降低可读性和可维护性[^1][^4]。 2. **`explicit` 构造函数的适用场景(禁止隐式转换)** * **防止意外转换:** **这是最主要、最推荐的原因。** 在你认为构造函数参数类型与类类型之间的转换不是显而易见、可能造成混淆或潜在错误时。 * **消除歧义:** 当类存在多个构造函数,且单参构造函数的隐式转换可能导致函数重载决议出现歧义时(虽然不如多参数转换常见)。 * **提高代码清晰度与安全性:** 强制要求显式转换,使得代码意图更清晰,明确地展示了类型转换的发生,避免了“魔法”转换。这使得代码更易于理解、维护,并能捕获潜在的类型错误[^3][^4]。 * **通用最佳实践:** C++ 标准库的设计和 C++ 核心指南 (C++ Core Guidelines) **强烈建议:除非有充分的理由需要隐式转换,否则所有单形参构造函数都应该声明为 `explicit`**[^4]。 * **缺点:** 代码需要写得稍微长一点(需要显式调用构造函数),在追求极致便利性的场景下可能感觉麻烦。不过,这种显式性带来的安全性和清晰性是值得的。 --- **三、关键结论与建议** 1. **区别本质:** `explicit` 专门用于控制单形参构造函数的**隐式转换行为**。普通单形参构造函数允许隐式转换,`explicit` 单形参构造函数禁止隐式转换。 2. **何时用 `explicit`:** **强烈推荐将绝大多数单形参构造函数声明为 `explicit`**。把它当作默认规则。只有当你能非常清晰地证明隐式转换对于该类的语义是自然的、安全的、不会引入歧义的,并且带来的便利性大大超过其风险时,才考虑不使用 `explicit`[^1][^3][^4]。 3. **拷贝构造函数:** 拷贝构造函数通常形式是 `A(const A& other)`。虽然它也是单形参构造函数,但将其声明为 `explicit` **是非常罕见且不自然的**。因为禁止隐式拷贝(像 `A a2 = a1;` 或者传值/返回值)会导致类基本无法正常使用。通常拷贝构造函数**不**使用 `explicit` 关键字[^2]。 4. **多参构造函数:** C++11 引入了花括号列表初始化 (`{}`)。对于多参构造函数(非拷贝构造),如果使用花括号初始化列表 (`MyClass obj{1, "str"};`) 进行初始化,编译器将其视为直接初始化,即使构造函数是 `explicit` 的。用等号加花括号 (`MyClass obj = {1, "str"};`) 则被视为拷贝初始化,需要构造函数是 *non-explicit*。不过,控制多参构造函数的隐式转换比较复杂,通常 `explicit` 主要关注点是单参构造函数的隐式转换。 --- **
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值