C++编译器被人如此诟病的主要原因就是C++背着用户干了太多的事情,而这些事情往往容易产生语义困惑(知道为什么溺爱孩子,给孩子一切都安排好,反而会受到不肖子的臭骂了吧??)
C++隐含做的事情很多,下面我就单从类型转化这个主题大概说一下C++都在类型转化过程中做了什么。不当之处请多批评指正:
1、将一个类转化成为其他类型:
类的作者可以定义该类的对象向指定数据类型的转换函数,具体使用方法是在类中定义
operator TypeName(),注意这样的类型转化函数虽然没有标明返回值,但是必须返回转化目标类型的
对象。函数不能有形参列表。
比如:
class String {
public: operator char*() { return _str; } char* _str; }
当需要将String转化成为char*的时候,比如 String[0]=‘a’;编译器就会尝试把String向支持下标操作的类型转化,看到有
一个char*类型支持下标操作,那么就根据用户定义的类型转化函数将其转化成为char* 相当于是_str[0]='a'
如果发现有多个转化函数都支持下标操作,那么编译不会通过。(同理也可以定义operator bool(),然后在
if(...)语句中调用这个向bool类型转化的函数)
2、将一个其他类型转化为本类型
相当于是operator TYPE的逆操作,本质上是由一个其他类型来构造本类型,其实现方式也是使用带一个其他类型作为形参的构造函数来做这个转化,比如:
class String { public: String(){} //如果下面一行是:explicit String(char* ch),那么就不能通过传值来调用构造函数 String(char* ch) { _string=new char[255]; printf("constructor\n"); strcpy(_string,ch); } ~String(){} char* _string; };
这样就可以把一个char* 直接通过调用String的构造函数来构造成为String(这个构造函数会在函数传值过程中构造局部对象)。
下面通过一个统一的例子来说明这两种转化:
#include <stdio.h> #include <string.h> class String { public: String(){} //如果下面一行是:explicit String(char* ch),那么就不能通过传值来调用构造函数 String(char* ch) { _string=new char[255]; printf("constructor\n"); strcpy(_string,ch); } ~String(){} String(String& str){} operator char*() { printf("convertor\n"); return _string; } operator int() { return _string[0]; } char* _string; }; void print(String ch) { printf(ch); } int main() { String str("abcde"); str[1]='z'; int a=str; print(str._string); return 0; }
分析一下上面这个例子:
1、String str("abcde") 首先使用String的带参数的构造函数初始化了String::_string
2、str[1]=‘z’ 编译器检测到这里需要将String类型转化为char*类型才能调用下标操作符,于是调用operator char*()
3、int a=str 同理,编译器检测到需要将String转化为int,于是调用operator int()
4、print(str._string) 注意这里调用的是void print(String ch),需要的参数是一个String对象(值传递),而传入的参数str._string是一个char*,这个时候编译器会查找到带参数的构造函数String(char*),调用这个构造函数把char*转化为一个String对象传递给print函数打印。
5、进入print函数以后,调用printf函数,这个函数需要一个char*指针,于是又把String转化为了char*,又调用了operator char*()
输出:
在第一步调用constructor,第二步调用operator char*(),第三步调用operator int(),
第四步:在给print传值的过程中由char*生成String的临时对象,又调用了constructor
第五步:进入printf后,又把临时的String对象转化成了char*,最后输出
总结:
1、把一个类的对象隐式的转化成为另一个类的对象,通过定义转化函数进行处理
2、通过一个类的对象构造另一个类的对象,调用对应单参数的构造函数
注:拷贝构造函数其实也是构造函数的一种(也会通过函数传值、函数返回、显示调用三种方式调用),只是通过另一个相同类的对象转化成为新对象而已。
C++智能指针问题:
智能指针维护了对象的引用次数,如果引用次数等于0,那么就释放这片内存,每次这个对象被赋值为其他对象,都会增加引用次数,每次调用析构函数就会减少一次引用次数。
传值问题:一般智能指针在实现的时候都会支持一个单参数(智能指针指向对象的指针)的构造函数,用普通的指针构造智能指针。这就遇到一个问题,当把一个普通指针传递给一个需要传入智能指针的函数时,会隐式调用智能指针的构造函数,初始化引用为1,但这个智能指针是一个局部变量,当函数结束的时候,智能指针发现引用从1变成了0,就会自动释放这片内存。这也许是程序员不想要的。办法是在智能指针的这个单参数构造函数前写上explicit,防止这种错误的发生,但share_ptr<Type> sp= new Type();这样的语法又不能用了,必须写成:share_ptr<Type> sp= share_ptr<Type>(new Type());又带来了麻烦,所以还是权衡一下吧,想要图方便,就会有风险.....