C++中explicit的用法
C++提供了关键字explicit,可以阻止不应该允许的经过转换构造函数进行的隐式转换的发生,声明为explicit的构造函数不能在隐式转换中使用。
C++中, 一个参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造函数), 承担了两个角色。
1 是个构造;2 是个默认且隐含的类型转换操作符。
所以, 有时候在我们写下如 AAA = XXX, 这样的代码, 且恰好XXX的类型正好是AAA单参数构造器的参数类型, 这时候编译器就自动调用这个构造器, 创建一个AAA的对象。
这样看起来好象很酷, 很方便。 但在某些情况下, 却违背了程序员的本意。 这时候就要在这个构造器前面加上explicit修饰, 指定这个构造器只能被明确的调用/使用, 不能作为类型转换操作符被隐含的使用。
解析:explicit构造函数是用来防止隐式转换的。请看下面的代码:
-
#include <iostream>
-
using namespace std;
-
class Test1
-
{
-
public :
-
Test1(int num):n(num){}
-
private:
-
int n;
-
};
-
class Test2
-
{
-
public :
-
explicit Test2(int num):n(num){}
-
private:
-
int n;
-
};
-
int main()
-
{
-
Test1 t1 = 12;
-
Test2 t2(13);
-
Test2 t3 = 14;
-
return 0;
-
}
编译时,会指出 t3那一行error:无法从“int”转换为“Test2”。而t1却编译通过。注释掉t3那行,调试时,t1已被赋值成功。
注意:当类的声明和定义分别在两个文件中时,explicit只能写在在声明中,不能写在定义中
22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
C++ Explicit Constructors(显式构造函数)
C++ 为类(Class)提供了许多默认函数。如果自己没有申明,编译器会为我们提供一个copy构造函数、一个copy assignment操作符和一个析构函数。此外,如果没有申明任何构造函数,编译器会为我们申明一个default构造函数。很像下面的Empty类:
1 class Empty{ 2 public: 3 Empty(); 4 Empty(const Empty &rhs); 5 ~Empty(); 6 Empty& operator=(const Empty &rhs); 7 };
就像Effective C++说的,如果不想使用编译器自动生成的函数,就应该明确拒绝。
1.C++中的隐式构造函数
如果c++类的其中一个构造函数有一个参数,那么在编译的时候就会有一个缺省的转换操作:将该构造函数对应数据类型的数据转换为该类对象。
2.Explicit Constructors显式构造函数
为了避免上面提到的只有一个参数的构造函数采用的缺省转换操作,在构造函数前,使用Explicit 关键字修饰即可。
3.如下面的例子:
1 #include <iostream> 2 using namespace std; 3 class B{ 4 public: 5 int data; 6 B(int _data):data(_data){} 7 //explicit B(int _data):data(_data){} 8 }; 9 10 int main(){ 11 B temp=5; 12 cout<<temp.data<<endl; 13 return 0; 14 }
程序第11行,将int转换为B类型的对象,就是使用了隐式构造函数。因为B中存在一个只有一个参数的构造函数,且参数类型也是int。
如果在构造函数前加explicit ,表示为显示构造函数,则第11行编译不能通过。因为这时,就没有隐式构造函数啦。
333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
定义函数时,后面加const意义
2014年05月12日 10:01:02 ranyongqing 阅读数:3270 标签: c++ 更多
个人分类: C++
const对象只能调用const成员函数。
const对象的值不能被修改,在const成员函数中修改const对象数据成员的值是语法错误
在const函数中调用非const成员函数是语法错误
任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。
以下程序中,类stack的成员函数GetCount仅用于计数,从逻辑上讲GetCount应当为const函数。编译器将指出GetCount函数中的错误。
class Stack
{
public:
void Push(int elem);
int Pop(void);
int GetCount(void) const; // const成员函数
private:
int m_num;
int m_data[100];
};
int Stack::GetCount(void) const
{
++ m_num; // 编译错误,企图修改数据成员m_num
Pop(); // 编译错误,企图调用非const函数
return m_num;
}
const成员函数的声明看起来怪怪的:const关键字只能放在函数声明的尾部,大概是因为其它地方都已经被占用了。
444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444
C++类型转换详解--const_cast
2011年02月28日 15:49:00 lwbeyond 阅读数:31136 标签: c++ class 更多
个人分类: C/C++
一. 函数描述:
const_cast < type-id > ( expression )
主要是用来去掉const属性,当然也可以加上const属性。主要是用前者,后者很少用。
去掉const属性:const_case<int*> (&num),常用,因为不能把一个const变量直接赋给一个非const变量,必须要转换。
加上const属性:const int* k = const_case<const int*>(j),一般很少用,因为可以把一个非const变量直接赋给一个const变量,比如:const int* k = j;
二. 使用范围:
1. 常量指针被转化成非常量指针,转换后指针指向原来的变量(即转换后的指针地址不变)。
-
class A
-
{
-
public:
-
A()
-
{
-
m_iNum = 0;
-
}
-
public:
-
int m_iNum;
-
};
-
void foo()
-
{
-
//1. 指针指向类
-
const A *pca1 = new A;
-
A *pa2 = const_cast<A*>(pca1); //常量对象转换为非常量对象
-
pa2->m_iNum = 200; //fine
-
//转换后指针指向原来的对象
-
cout<< pca1->m_iNum <<pa2->m_iNum<<endl; //200 200
-
//2. 指针指向基本类型
-
const int ica = 100;
-
int * ia = const_cast<int *>(&ica);
-
*ia = 200;
-
cout<< *ia <<ica<<endl; //200 100
-
}
2. 常量引用转为非常量引用。
-
class A
-
{
-
public:
-
A()
-
{
-
m_iNum = 1;
-
}
-
public:
-
int m_iNum;
-
};
-
void foo()
-
{
-
A a0;
-
const A &a1 = a0;
-
A a2 = const_cast<A&>(a1); //常量引用转为非常量引用
-
a2.m_iNum = 200; //fine
-
cout<< a0.m_iNum << a1.m_iNum << a2.m_iNum << endl; //1 1 200
-
}
2. 常量对象(或基本类型)不可以被转换成非常量对象(或基本类型)。
[c-sharp] view plain copy
- void foo()
- {
- //常量对象被转换成非常量对象时出错
- const A ca;
- A a = const_cast<A>(ca); //不允许
- const int i = 100;
- int j = const_cast<int>(i); //不允许
- }
记住这种转换只是开了一个接口,并不是实质上的转换。(其实也算是实质上的转换了,只不过表达上不允许这样写)
3. 添加const属性
[c-sharp] view plain copy
- int main(int argc, char ** argv_)
- {
- int i = 100;
- int *j = &i;
- const int *k = const_cast<const int*>(j);
- //const int *m = j; 感觉和这样写差不多
- //指的地址都一样
- cout <<i<<","<<&i<<endl; //100, 0012FF78
- cout <<*j<<","<<j<<endl; //100, 0012FF78
- cout <<*k<<","<<k<<endl; //100, 0012FF78
- *j = 200;
- //*k = 200; //error
- return 0;
- }
三. 总结:
1. 使用const_cast去掉const属性,其实并不是真的改变原类类型(或基本类型)的const属性,它只是又提供了一个接口(指针或引用),使你可以通过这个接口来改变类型的值。也许这也是const_case只能转换指针或引用的一个原因吧。
2. 使用const_case添加const属性,也是提供了一个接口,来不让修改其值,不过这个添加const的操作没有什么实际的用途(也许是我认识太浅了)。
5555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555
C++类中const函数与非const函数的调用规则
2016年05月01日 17:04:52 langzi989 阅读数:3570
class EX{
public:
void constFunction() const;
};
- 1
- 2
- 3
- 4
如上所示,通过将类成员函数声明为const,以表示这个函数不可以修改类对象。任何不可以修改数据成员的函数都应该声明为const,如果在编写const成员函数时,不慎修改了数据成员, 或者调用了其他的非const函数,则此时编译器会指出错误,这样做的函数是提高了程序的健壮性。
在相同的函数参数及相同的名字的情况下,const函数与非const函数可以构成重载函数,但是const成员函数不能改变任何的非静态变量
const函数与非const函数的调用规则
- const对象默认调用const成员函数,非const对象默认调用非const成员函数;
- 若非const对象想调用const成员函数,则需要显示的转化,例如(const Student&)obj.getAge();
- 若const对象想调用非const成员函数,同理进行强制类型转换const_cast < Student&>(constObj).getAge();(注意constObj一定要加括号)
当类中只有一种函数存在的情况
- 非const对象可以调用const成员函数或者非const成员函数
- const对象只能调用const成员函数,若直接调用非const成员函数编译器会报错。
55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555
C++强制类型转换操作符 const_cast
const_cast也是一个强制类型转换操作符。《C++ Primer》中是这样描述它的:
1.将转换掉表达式的const性质。
2.只有使用const_cast才能将const性质性质转化掉。试图使用其他三种形式的强制转换都会导致编译时的错误。(添加const还可以用其他转换符,如static_const)
3.除了添加const或删除const特性,使用const_cast符来执行其他任何类型的转换都会引起编译错误。(volatile限定符也包括,不过我不怎么了解,本文主要说const)
对于第一点,转换掉表达式的const性质,意思是可以改变const对象的值了吗?一开始我的确是这样子认为的,于是我敲出了如下的代码:
int main() { const int constant = 26; const int* const_p = &constant; int* modifier = const_cast<int*>(const_p); *modifier = 3; cout<< "constant: "<<constant<<endl; cout<<"*modifier: "<<*modifier<<endl; system("pause"); }
然而程序并没有像预想的那样输出两个3,运行结果是这样的:
看来C++还是很厚道的,对声明为const的变量来说,常量就是常量,任你各种转化,常量的值就是不会变。这是C++的一个承诺。
那既然const变量的值是肯定不会发生变化的,还需要这个const_cast类型转化有何用?这就引出了const_cast的最常用用法:
如果有一个函数,它的形参是non-const类型变量,而且函数不会对实参的值进行改动,这时我们可以使用类型为const的变量来调用函数,此时const_cast就派上用场了。
例如:
void InputInt(int * num) { cout<<*num<<endl; } int main() { const int constant = 21; //InputInt(constant); //error C2664: “InputInt”: 不能将参数 1 从“const int”转换为“int *” InputInt(const_cast<int*>(&constant)); system("pause"); }
除此之外,还有另外一种情况const指针能够派上用场。如果我们定义了一个非const的变量,却使用了一个指向const值的指针来指向它(这不是没事找事嘛),在程序的某处我们想改变这个变量的值了,但手头只持有指针,这是const_cast就可以用到了:
int main() { int constant = 26; const int* const_p = &constant; int* modifier = const_cast<int*>(const_p); *modifier = 3; cout<< "constant: "<<constant<<endl; cout<<"*modifier: "<<*modifier<<endl; system("pause"); }
总结一下上文:const_cast绝对不是为了改变const变量的值而设计的!
在函数参数的传递上const_cast的作用才显现出来。
>>>>>>>>>>>>>>>>>> >>>分割线>>>>>>>>>>>>>>>>>>>>>>>>>
const_cast中的未定义行为
上面的第一段程序,输出变量constant与*modefier的地址后....
int main() { const int constant = 26; const int* const_p = &constant; int* modifier = const_cast<int*>(const_p); *modifier = 3; cout<< "constant: "<<constant<<" adderss: "<< &constant <<endl; cout<<"*modifier: "<<*modifier<<" adderss: " << modifier<<endl; system("pause"); }
运行结果:
它们的地址是一样的,值却不同。具体原因我还是不大清除。在另外一些博客中看到, *modifier = 3; 这种操作属于一种“未定义行为”,也即是说操作结果C++并没有明确地定义,结果是怎样的完全由编译器的心情决定。对于未定义的行为,我们只能避免之。
关于const_cast是否安全的讨论
逛了一些网站,大致有如下观点:
const_cast is safe only if you're casting a variable that was originally non-const. For example, if you have a function that takes a parameter of a const char *, and you pass in a modifiable char *, it's safe to const_cast that parameter back to a char * and modify it. However, if the original variable was in fact const, then using const_cast will result in undefined behavior. 也即是上文中所说的const_cast的二种适用情况。
I would rather use static cast for the adding constness: static_cast<const sample*>(this). When I'm reading const_cast it means that the code is doing something potentially dangerous, so i try to avoid it's use when possible. 也有人认为const_cast本身就给潜在危险带来可能,所以还是尽可能不用它了。 当需要给变量添加const属性时,使用更为安全的static_cast来代替const_cast。 这里附上讨论链接。const_cast是否安全?
777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777
const T、const T*、T *const、const T&、const T*& 的区别
原文地址: http://blog.csdn.net/luoweifu/article/details/45600415
这里的T指的是一种数据类型,可以是int、long、doule等基本数据类型,也可以是自己类型的类型class。单独的一个const你肯定知道指的是一个常量,但const与其他类型联合起来的众多变化,你是不是就糊涂了?下面我们一一来解析。
const T
定义一个常量,声明的同时必须进行初始化。一旦声明,这个值将不能被改变。
int i = 5;
const int constInt = 10; //正确
const int constInt2 = i; //正确
constInt = 20; //错误,常量值不可被改变
const int constInt3; //错误,未被初
const T*
指向常量的指针,不能用于改变其所指向的对象的值。
const int i = 5;
const int i2 = 10;
const int* pInt = &i; //正确,指向一个const int对象,即i的地址
//*pInt = 10; //错误,不能改变其所指缶的对象
pInt = &i2; //正确,可以改变pInt指针本身的值,此时pInt指向的是i2的地址
const int* p2 = new int(8); //正确,指向一个new出来的对象的地址
delete p2; //正确
//int* pInt = &i; //错误,i是const int类型,类型不匹配,不能将const int * 初始化为int *
int nValue = 15;
const int * pConstInt = &nValue; //正确,可以把int *赋给const int *,但是pConstInt不能改变其所指向对象的值,即nValue
*pConstInt = 40; //错误,不能改变其所指向对象的值
const int* 与int* const的区别
指针本身就是一种对象,把指针定义为常量就是常量指针,也就是int* const的类型,也可以写成int *const,声明时必须初始化。
int nValue = 10;
int* const p = &nValue;
int *const p2 = &nValue;
const int* 指针指向的对象不可以改变,但指针本身的值可以改变;int* const 指针本身的值不可改变,但其指向的对象可以改变。
int nValue1 = 10;
int nValue2 = 20;
int* const constPoint = &nValue1;
//constPoint = & nValue2; //错误,不能改变constPoint本身的值
*constPoint = 40; //正确,可以改变constPoint所指向的对象,此时nValue1 = 40
const int nConstValue1 = 5;
const int nConstValue2 = 15;
const int* pPoint = &nConstValue1;
//*pPoint = 55; //错误,不能改变pPoint所指向对象的值
pPoint = &nConstValue2; //正确,可以改变pPoint指针本身的值,此时pPoint邦定的是nConstValue2对象,即pPoint为nConstValue2的地址
const int* const 是一个指向常量对象的常量指针,即不可以改变指针本身的值,也不可以改变指针指向的对象。
const int nConstValue1 = 5;
const int nConstValue2 = 15;
const int* const pPoint = &nConstValue1;
//*pPoint = 55; //错误,不能改变pPoint所指向对象的值
//pPoint = &nConstValue2; //错误,不能改变pPoint本身的值
const T&
对常量(const)的引用,又称为常量引用,常量引用不能修改其邦定的对象。
int i = 5;
const int constInt = 10;
const int& rConstInt = constInt; //正确,引用及邦定的值都是常量
rConstInt = 5; //错误,不能改变引用所指向的对象
允许为一个常量引用邦定一个非常量对象、字面值,甚至是表达式;引用的类型与引用所指向的类型必须一致。
int i = 5;
int& rInt = i; //正确,int的引用
const int constInt = 10;
const int& rConstInt = constInt; //正确,引用及邦定的值都是常量
const int& rConstInt2 = rInt; //正确,用rInt邦定的对象进行赋值
rInt = 30; //这时,rConstInt2、rInt、i的值都为30
//rConstInt2 = 30; //错误,rConstInt2是常量引用,rConstInt2本身不能改变所指向的对象
int i2 = 15;
const int& rConstInt3 = i2; //正确,用非常量的对象为其赋值
const int& rConstInt4 = i + i2; //正确,用表达式为其赋值,值为45
i = 20; //此时i=20, rInt = 20, rConstInt4 = 45,说明rConstInt4邦定的是i + i2的临时变量
const int& rConstInt5 = 50; //正解,用一个常量值为其赋值
const T*&与T *const&
指向常量对象的指针的引用,这可以分两步来理解:1.const T*是指向常量的指针;2.const T*&指向常量的指针的引用。
const int nConstValue = 1; //常量对象
const int nConstValue2 = 2; //常量对象
const int* pConstValue = &nConstValue; //指向常量对象的指针
const int* pConstValue2 = &nConstValue2; //指向常量对象的指针
const int*& rpConstValue = pConstValue; //指向常量对象的指针的引用
//*rpConstValue = 10; //错误,rpConstValue指向的是常量对象,常量对象的值不可改变
rpConstValue = pConstValue2; //正确,此时pConstValue的值等于pConstValue2
//指向常量对象的指针本身是对象,引用可以改变邦定对象的值
int nValue = 5;
int nValue2 = 10;
int *const constPoint = &nValue; //常量指针
int *const constPoint2 = &nValue2; //常量指针
int *const &rpConstPoint = constPoint; //对常量指针的引用,邦定constPoint
//rpConstPoint = constPoint2; //错误,constPoint是常量指针,指针本身的值不可改变
*rpConstPoint = 20; //正确,指针指向的对象可以改变
在函数中的应用
我们直接从需求出来,假设有这样一个数据结构:
typedef struct __Data
{
int value;
public:
__Data()
:value(0){}
}Data;
1.希望传入一个对象,又不想让函数体修改这个对象。
方式<1>
void Dealwith(const Data& data)
{
cout << data.value << endl;
//data.value = 5; //错误,data是常量引用,不能改变其邦定的对象
}
这种方式还有一个好处是只有在调用函数的时候会邦定对象,传递的是对象的引用,而不是对象,减少函数调用时对象赋值的花销。
方式<2>
void Dealwith(const Data* pData)
{
cout << pData->value << endl;
//pData->value = 5; //错误,pData是指向常量对象的指针,不能改变其指向的对象
}
这种方式与void Dealwith(const Data& data)的功能相同
方式<3>
Data g_data(20);
void Dealwith(const Data*& pData)
{
cout << pData->value << endl;
//pData->value = 5; //错误,pData邦定的是指向常量对象的指针,常量对象的指针不能改变其指向的对象
pData = &g_data; //正确,pData是[指向常量对象的指针]的引用,引用可改变其邦定的对象
}
调用如下:
Data d(10);
const Data* pData = &d;
Dealwith(pData);
cout << pData->value << endl; //此时pData->value为20,d.value还是10
这种方式函数未改变传入的对象的值,但可以返回另外一个对象的指针。注意返回的指针必须指向全局的对象,如果返回函数内定义的对象,退出函数作用域后,其指针将无效,这是非常危险的;如果Dealwith是成员函数,也可以返回指向成员的指针。
2.在类中的使用,返回一个类的成员,但不希望调用方修改这个成员。
方式<1>
class MyData
{
public :
MyData(std::string name, Data data)
{
m_name = name;
m_data = data;
}
const Data* GetData()
{
return &m_data;
}
private:
std::string m_name;
Data m_data;
};
调用如下:
MyData mydata("", Data(100));
const Data* pData = mydata.GetData();
cout << pData->value << endl; //pData->value = 100
//pData->value = 50; //错误,pData是指向常量对象的指向,不能改变其指向对象的值
方式<2>
有人可能会问GetData也可以写成这样:
const Data& GetData()
{
return m_data;
}
这样的话,调用方常常容易写成这样:
MyData mydata("", Data(100));
Data data = mydata.GetData(); //这会有个赋值的过程,会把mydata.m_data赋给data
cout << data.value << endl; //data.value = 100
data.value = 50; //正确,data.value=50,但mydata.m_data.value还是100
这样调用时会有一个结果赋值的过程,如果Data是一个复杂的类,会有较大的开销,其效果与下面这种方式是一样的:
Data GetData()
{
return m_data;
}
当然,如果调用方这样使用是正确的:
const Data& GetData()
{
return m_data;
}
MyData mydata("", Data(100));
const Data& data = mydata.GetData(); //这会有个赋值的过程,会把mydata.m_data赋给data
cout << data.value << endl; //data.value = 100
//data.value = 50; //错误,data是一个对常量的引用,不能改变其邦定的对象
这对调用方的技术能力要求比较高,如果你是设计方,一定要尽量使接口简单易用。
方式<3>
如果你要传入一个Data进行一些处理,处理完后返回类的成员m_data,可如下实现:
void DoSomething(const Data*& pData)
{
if (pData != NULL)
{
// doto: 这里实现你要处理的功能
}
pData = &m_data;
}