【C++探秘】 部类 强制 转化 规则: 利用 等号+明值 直接初始化自定义类
本文主要讲两个问题:
(1) 为什么我们在编写代码的时候,有时候强制转换能成功,有些类型却不可以? 这是C++内核规则 还是 用户自定义规则?
(2)怎么使用 内核控制的 整数或者字符串明值来初始化自定义类对象? 比如 self_defined_class a=100; self_defined_class a="123"; 而不是 self_defined_class a(123) 或者 self_defined_class a(“123”);
第一个问题的答案是: 强制类型转化 的规则是内核定义+用户自定义的,也就是部分规则是内核定义,部分支持扩展,而不是内核限死的规则。
两个类型对象是否能够强制转换,跟对象大小没有绝对的关系。
【规则1】默认规则中,只有大对象向小对象转换,就像子类对象能赋值给父类对象,但是父类对象无法赋值给子类对象 。例如:
class A { public: int a; } ; class B:public A { public: int b; } ;对于这样的一个类,以下写法正确:
A aa; B bb; aa=bb;但是如果反过来赋值就是错误的,以下写法错误:
A aa; B bb; bb=aa;
如果一定要用父类来初始化子类对象呢?? 其实 也可以,而且很简单。只需要给B添加个构造函数,如下:
class A { public: int a; } ; class B:public A { public: int b; B(){b=0;} B(A aa){b=aa.a;a=aa.a;} } ;这样定义之后,上述两种写法都正确。
我们可以这样理解: C++ 内核规则为什么只实现了子类对象向父类对象的赋值,而没有实现父类对象像子类对象赋值?? 答案是:我们可以这样理解,C++把用户不能做的,在内核规则中以另一种方式给做了。我们可以想象,如果C++内核 不实现子类对象向父类对象赋值,那么再牛逼的程序员也无法用C++实现。我们可以看下如果 C++不实现,那么我们应当用以下代码来实现:
class A { public:
A(B b){a=b.a;} int a; } ; class B:public A { public: int b; B(){b=0;} B(A aa){b=aa.a;a=aa.a;} } ;而,显而易见的是,这代码是不可能的,因为B 是A的派生类,定义必须在A之后,而定义A的时候还没有B,又哪来的A(B b){a=b.a;}呢?
由此我们可以看出C++内核规则的精炼,给程序员最大的自由。规则用于解决一些矛盾,而不会限制别人。
这里我们再讨论一个知识点,那就是:为什么需要 拷贝构造函数?
C++设计者在初期的时候,要实现同类对象之间可以相互赋值。根据以上的分析,如果使用普通的构造函数如
B(B b){ ...}
因为b也是个B类实例,临时变量用来接受传入的参数,会在申明实例时同时产生,这样的结果是导致无限循环。所以聪明的设计者们发明了 Copy Constructor
B(B&b){....}
这样以来,传值方式改为传名, b对象的类型的实质变成了 B& 而不是B, 这样也不会产生临时对象变量。也不会触发新的构造函数了。
默认的:
【规则2】 C++会为每个自定义类默认的添加拷贝构造函数,从而支持同类型对象之间的相互转换。
(2) 在知道了以上知识后,我相信,问题二很简单,只需要为你的类添加一个构造函数,就可以把C++内核变量int ,float,string等等直接 用等号= 来赋值,这是很酷的一件事。
那么为何 用 = 号 就能够触发构造函数呢? 毕竟没有申明新的类型对象啊??
答案是C++内核的又一条规则:
【规则3】C++会为每个自定义类默认的添加一个 “等号=”重载函数, 用于同类型对象之间的相互等号赋值。
=号重载函数的参数是右值,应当是一个const B& 或者B 变量,在赋值运算前,会首先执行强制转换,如果成功,那么赋值,如果没定义,那么编译不通过。
通过今天的讨论,我们知道,其实C++的内核规则 的核心部分很简单,就是基本文法+ 基本类型。 其他的规则都是用来给这个语言系统打补丁,用来解决各种矛盾(或者叫做问题),我把它叫做“支撑规则”。所以,如果有一本C++的书,能够不要从繁琐的“支撑规则”说起,而是从最简单的语句开始,一个个的解释为什么要有这条规则。我相信,C++会变得不那么神秘。