显示初始化、隐式初始化和赋值
最近看了不少关于初始化和赋值的文章,其中提到最多的是要分清楚什么是赋值,什么是初始化,下面的例子都是初始化,也就是显示初始化和隐式初始化:
C++隐式初始化
int ival(1024);
string hello("Hello world.")
C++显式初始化
int ival = 1024;
string hello = "Hello world."
Note: 注意这里"=" 语法是调用拷贝构造函数而不是赋值运算,也就是这是一个初始化操作,而不是赋值操作,因为赋值操作是在一个对象已经存在的情况下进行的,而这里在=那句话之前对象并不存在。下面是赋值的例子:
string hello;
hello = "Hello world"; <- 这里hello 已经被定义了,才是赋值操作.
隐式和显示类型转换
先搞清楚什么是隐式类型转换,什么是显示类型转换吧!所谓隐式类型转换就是隐藏的,代码中并不有语句进行转换,但是编译器会隐式做类型转换以满足一些特定语句的需要,比如:int i = i +d;(i是int,d是double),这里发生的隐式转换包括,先将i转为double,和d相加,然后把i+d的结果转成int。更概括来说,隐式类型转换发生在以下4中情况中:
- 在混合类型的算术表达式中
int ival = 3;
double dval = 3.1415
ival + dval; //ival 被提升为double 类型:3.0
- 用另外一种类型的表达式赋值
int *pi = NULL; // NULL(0)被转换成了int* 类型的空指针值
- 用一个表达式传递给一个函数调用
extern double sqrt(double);
sqrt(2); //2被提升为double类型: 2.0
- 从一个函数返回一个表达式
double difference(int ival1, int ival2)
{
return ival1 - ival2; //返回值被提升为double 类型.
}
C++内建类型(char,int,short,double,etc.)对像之间默认含有隐式转换,C++的自定义类对象之间可以含有隐式转换,隐式转换有两种形式:从其他类型向自身转换,从自身向其他类型转换。从其他类型向自身转换是由下面的conversion constructor完成的,这个conversion constructor也是构造函数的一种,因为只有一个参数而变成了conversion constructor,但有时我们不想这个构造函数变成conversion constructor,就可以加explicit来明确禁止进行隐式类型转换; 从自身向其他类型转换就是由隐式类型转换操作符完成的。
C++显式转换包含四种转换:
- static_cast : 编译期的转化,不能转换掉expression的const、volitale、或者__unaligned属性
- 所有内建类型对象之间的隐式转换都可用static_cast.
- 把空指针转换成目标类型的空指针用static_cast。
- 把任何类型的表达式转换成void类型用static_cast。
- 类层次间的上行转换和下行转换也可以用static_cast,但下行转换即当把基类指针或引用转换成子类表示时,由于没有动态类型检查,所以是不安全的.反之是安全的.
- dynamic_cast : 运行期的转换,类层次间的上行转换和下行转换
- dynamic_cast具有类型检查的功能,下行转换的效果跟static_cast是一样的,但下行转换比static_cast更安全。
- dynamic_cast还支持交叉转换,两个类如果有共同的祖先,他们的指针就可以用dynamic_cast.
- const_cast : 编译期的转化,类型中的常量
- reinterpret_cast : 任何指针都可以转换成其它类型的指针,可用于如char* 到 int*,或者One_class* 到 Unrelated_class* 等的转换,因此可能是不安全的。
在这里,需要特别说明一下static_cast,对于用户自定义的类型static_cast会调用用户自定义的类型转换操作符,如果用户没有定义类型转换操作符,static_cast会失败,比如类A和类B没有关系,但是强制调用static_cast,编译器会报告:error: no matching function for call to B::B(A&)。C++11开始支持explicit的类型转换符,也就是这个类型转换符必须通过static_cast调用。
Conversion Constructors
A constructor that can be called with a single argument is used for conversions from the type of the argument to the class type. Such a constructor is called a conversion constructor.
Sometimes a conversion is required but no conversion constructor exists in the class. These conversions cannot be performed by constructors. The compiler does not look for intermediate types through which to perform the conversion. For example, suppose a conversion exists from type Point to type Rect and a conversion exists from type int to type Point. The compiler does not supply a conversion from type int to type Rect by constructing an intermediate object of type Point.
Explicit Type Conversion Operator: ()
C++ allows explicit type conversion using a syntax similar to the function-call syntax.
simple-type-name ( expression-list )
A simple-type-name followed by an expression-list enclosed in parentheses constructs an object of the specified type using the specified expressions. The following example shows an explicit type conversion to type int:
int i = int( d );
需要特别注意的是,显示类型转换操作符只限于简单类型的转换,用户自定义类型以前是不可以的,但是从C++11开始,也可以了。显示类型转换符的意义是不允许隐式类型转换的情况下调用该类型转换函数
#include < string>
using namespace std;
struct A
{
// implicit conversion to int
operator int() { return 100; }
// explicit conversion to std::string
explicit operator std:: string()
{
cout<< " to string "<<endl;
return " explicit ";
}
};
int main()
{
A a;
int i = a; // ok - implicit conversion
std:: string s1 = a; // error - requires explicit conversion
std:: string s2 = static_cast<std:: string>(a);
}