C++显式构造函数 [翻译]

原文地址:http://www.codeproject.com/KB/cpp/explicit_constructor_CPP.aspx
简介

构造函数的确能使你的类变的简单优雅,它也是一个基本的函数,可以由编译器以默认的方式提供给你,即默认构造函数。在c++中,如果你只写一行代码。
Class A{};
编译器会提供给你一个默认的、无参数的构造函数,并伴随一个析构函数,一个拷贝构造函数(copy Constructor),一个拷贝复制操作符(copy assignment operator)

背景

那么什么是显式构造函数?
为了方便理解显式构造函数,我们首先需要理解隐式转换,让我们看一下它在代码中的作用,以及什么时候不需要这些特性。
使用以下代码,例1:思考下面这个传统而简单的复数类:

  1. #include <iostream>
  2. using std::cout;
  3. using std::endl;
  4. class complexNumbers {
  5.   double real, img;
  6. public:
  7.   complexNumbers() : real(0), img(0) { }
  8.   complexNumbers(const complexNumbers& c) { real = c.real; img = c.img; }
  9.   complexNumbers( double r, double i = 0.0) { real = r; img = i; }
  10.   friend void display(complexNumbers cx);
  11. };
  12. void display(complexNumbers cx){
  13.   cout<<"Real Part: "<<cx.real<<" Imag Part: "<<cx.img<<endl;
  14. }
  15. int main() {
  16.   complexNumbers one(1);
  17.   complexNumbers five = 5;
  18.   display(one);
  19.   display(five);
  20.   return 0;
  21. }

 

这个复数类很简单,它包括实数复数两部分,代码定义了一个默认的构造函数,一个拷贝构造函数,以及着重定义了另一个构造函数,用它来帮我们做一些隐式的构造。
在main函数中,我们首先创建了一个名为one 和一个名为 five的对象,由于隐式转换的作用,它们都能成功调用。故对象one的实数为1,虚数为 0;对象five的实数为5,虚数0.我们可以使用复数类中的 “display”方法打印出来,对隐式转换的理解,这是一个很好的例子。输出结果如下:

Real Part: 1 Imag Part: 0
Real Part: 5 Imag Part: 0

 

下面我们考虑这个例子:

  1. #include <iostream>
  2. using std::cout;
  3. using std::endl;
  4. class complexNumbers {
  5.   double real, img;
  6. public:
  7.   complexNumbers() : real(0), img(0) { }
  8.   complexNumbers(const complexNumbers& c) { real = c.real; img = c.img; }
  9.   complexNumbers( double r, double i = 0.0) { real = r; img = i; }
  10.   friend void display(complexNumbers cx);
  11. };
  12. void display(complexNumbers cx){
  13.   cout<<"Real Part: "<<cx.real<<" Imag Part: "<<cx.img<<endl;
  14. }
  15. int main() {
  16.   complexNumbers one(1);
  17.   display(one);
  18.   display(300);
  19.   return 0;
  20. }

 

类似的例子,只是在main方法中多加了一行,display(300),继续,输出结果变为:

Real Part: 1 Imag Part: 0
Real Part: 300 Imag Part: 0
 

“邦”!!!这不是我们希望的结果,首先代码本身就不清楚,display(300)什么意思?display 方法的参数期待的是复数类的一个对象/实例,而300根本不是,那么这种情况是如何发生的呢?
因为display方法期待一个复数类的一个对象/实例,当我们把300这个十进制数传进去是,在将300这个十进制数传到复数类的一个临时对象时,发生了一次隐式转换。(因此,300就赋值给临时对象的实数了)。
那么怎么解决这种情况呢??
很简单,强制编译器只能通过显示构造来创建对象,技巧如下:

  1. #include <iostream>
  2. using std::cout;
  3. using std::endl;
  4. class complexNumbers {
  5.   double real, img;
  6. public:
  7.   complexNumbers() : real(0), img(0) { }
  8.   complexNumbers(const complexNumbers& c) { real = c.real; img = c.img; }
  9.   explicit complexNumbers( double r, double i = 0.0) { real = r; img = i; }
  10.   friend void display(complexNumbers cx);
  11. };
  12. void display(complexNumbers cx){
  13.   cout<<"Real Part: "<<cx.real<<" Imag Part: "<<cx.img<<endl;
  14. }
  15. int main() {
  16.   complexNumbers one(1);
  17.   display(one);
  18.   complexNumbers two =2;
  19.   display(200);
  20.   return 0;
  21. }

 

思考下面的语句:

  1. explicit complexNumbers( double r, double i = 0.0) { real = r; img = i; }

此时我们强制编译器在这段代码中不能使用隐式转换,如果程序员写包含隐式转换的代码,编译器将返回一个错误。

  1. bash-3.2$ g++ -g -o hello test2.cpp 
  2. test2.cpp: In function ‘int main()’:
  3. test2.cpp:22: error: conversion from ‘int’ to non-scalar type ‘complexNumbers’ requested
  4. test2.cpp:23: error: conversion from ‘int’ to non-scalar type ‘complexNumbers’ requested

 

鉴于此,程序员应当用下面的方法实现:
display(complexNumbers(200)); //只允许显示转换…………

 

 

<think>嗯,用户问的是“构造函数”,这应该和C++里的explicit关键字有关吧。之前用户已经问过关于explicit的一般概念,现在具体到构造函数了。我需要先回忆C++中的构造函数默认是隐转换的,也就是说,如果构造函数只有一个参数,编译器可能会自动进行隐类型转换,这有时候会导致意想不到的问题。 首先,我应该解释构造函数的作用,即防止隐转换。然后举例子说明没有explicit的时候会发生什么,加上之后有什么不同。比如用一个类MyClass构造函数接受int参数,如果不用explicit,当调用MyClass obj = 5;的时候,编译器会自动转换成MyClass的对象,而用了explicit之后,这种隐转换就会报错,必须调用构造函数。 还要注意,构造函数主要用于防止单参数构造函数的隐转换,但在C++11之后,多参数的构造函数如果使用了explicit,也可以防止用初始化列表进行的隐转换。比如MyClass(int a, int b),如果explicit,那么不能直接写MyClass obj = {1,2}; 必须地创建对象。 另外,需要提到使用构造函数的好处,比如提高代码安全性,避免意外的类型转换导致的错误,增加代码的可读性,明确开发者的意图。可能还要对比隐转换的缺点,比如隐藏的错误,调试困难等。 用户可能是在学习C++的时候遇到了这个问题,或者在代码中发现了隐转换导致的bug,所以想了解如何避免。他们可能已经有一定的C++基础,但对explicit关键字的具体应用场景还不清楚。需要确保解释清晰,例子简单明了,让用户能够快速理解应用场景和重要性。 还要检查术语的准确性,比如构造函数是否只针对单参数,或者多参数的情况,是否需要提到C++11的变化。确保回答结构合理,分点说明,可能用代码示例来辅助解释,这样用户更容易理解。 总结下来,回答的结构应该是:定义构造函数,解释其作用,对比有无explicit的情况,举例子,说明使用场景和好处,最后总结。这样用户就能全面了解这个概念了。</think>好的!**构造函数(Explicit Constructor)** 是 C++ 中的一个重要概念,用于防止编译器进行**隐类型转换**,从而避免潜在的逻辑错误。以下是详细解释: --- ### 一、核心概念 - **隐转换问题**:默认情况下,C++ 中如果类的构造函数可以通过单个参数调用(或通过多参数构造函数的隐转换),编译器会自动进行类型转换。 - **构造函数的作用**:用 `explicit` 关键字标记构造函数,强制要求用户必须调用构造函数,禁止隐转换。 --- ### 二、例子对比(有 vs 无 `explicit`) #### 1. **无 `explicit` 的构造函数** ```cpp class MyClass { public: MyClass(int x) { // 隐构造函数 std::cout << "构造 MyClass,x = " << x << std::endl; } }; int main() { MyClass obj1 = 5; // 隐转换:将 int 5 转成 MyClass 对象 MyClass obj2(10); // 调用构造函数 return 0; } ``` **输出**: ``` 构造 MyClass,x = 5 构造 MyClass,x = 10 ``` **问题**:`MyClass obj1 = 5;` 这种隐转换可能掩盖逻辑错误(例如意外传递错误类型的参数)。 --- #### 2. **有 `explicit` 的构造函数** ```cpp class MyClass { public: explicit MyClass(int x) { // 构造函数 std::cout << "构造 MyClass,x = " << x << std::endl; } }; int main() { // MyClass obj1 = 5; // 错误:禁止隐转换 MyClass obj1(5); // 正确:调用 MyClass obj2 = MyClass(10); // 正确:构造 return 0; } ``` **关键点**:必须调用构造函数,避免隐转换。 --- ### 三、应用场景 1. **防止意外的类型转换**: - 例如,`std::vector` 的构造函数 `explicit vector(size_type count)`,避免误写 `vector<int> v = 10;`(实际应写作 `vector<int> v(10)`)。 2. **提高代码安全性**: - 在传递参数或赋值时,强制类型匹配,减少隐藏错误。 3. **多参数构造函数C++11 起)**: ```cpp class Point { public: explicit Point(int x, int y) {} // C++11 支持多参数 explicit }; int main() { // Point p = {1, 2}; // 错误:禁止隐转换 Point p(1, 2); // 正确 } ``` --- ### 四、总结 - **何时使用**:若构造函数可能导致歧义或意外行为,建议标记为 `explicit`。 - **优点**:代码更安全、意图更明确。 - **注意**:对单参数构造函数最常用,但 C++11 后也适用于多参数构造函数。 理解构造函数是写出健壮 C++ 代码的重要一步! 🚀
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值