1.C++中有哪4个和类型转换相关的关键字?这些关键字都有什么特点?应该在哪些场合下使用.
C语言中的强制类型转换可以随意的转换我们想要的类型了,格式如下(类型) 变量名;
为什么c++还要引入新的4种类型转换呢?
这是因为新的类型转换控制符可以很好的控制类型转换的过程,允许控制各种类型不同的转换.
还有一点好处是C++的类型转换控制符能告诉程序员或读者我们这个转换的目的是什么,我们
只要看一下代码的类型转换控制符,就能明白我们想要达到什么样的目的.
1)static_cast <T*> (content) 静态转换.在编译期间处理
它主要用于C++中内置的基本数据类型之间的转换.但是没有运行时类型的检测来保证转换
的安全性.
为什么需要static_cast类型的转换?
a.用于基类和子类之间的指针或引用的转换。这种转换把子类的指针或引用转换为基类表示是安全的;
进行下行转换,把基类的指针或引用转换为子类表示时,由于没有进行动态类型检测,所以是不安全的;
b.把void类型的指针转换成目标类型的指针(不安全).
c.用于内置的基本的数据类型之间的转换.
d.把任何类型的表达式转换成void类型的.
注意:static_cast不会转换掉content的const,volatile,__unaligned属性
2)static_const<T*>(content):去常转换;编译时执行;
它主要作用同一个类型之间的去常和添加常属性之间的转换.不能用做不同的类型之间的转换.
它可以把一个不是常属性的转换成常属性的,同时它也可以对一个本是常属性的类型进行去常.
3)dynamic_cast<T*>(content) 动态类型转换;也是向下安全转型;是在运行的时候执行;
通常用于基类和派生类之间的转换.转换时会进行类型安全检查。
a.不能用于内置的基本数据类型之间的转换.
b.dynamic_cast转换成功的话返回的是类的指针或引用,失败返回null;
c.dynamic_cast进行的转换的时候基类中一定要有虚函数,因为只有类中有了虚函数,
才说明它希望让基类指针或引用指向其派生类对象的情况,这样才有意义.
这是由于运行时类型检查需要运行时类型的信息,而这些信息存储在虚函数表中.
d.在类的转换时,在类层次间进行转换的时候,dynamic_cast和static_cast进行上行转换
的时候效果是一样的;但是在进行下行转换的时候,dynamic_cast会进行类型检查所以
它更安全.它可以让指向基类的指针转换为指向其子类的指针或是其兄弟类的指针;
4)reinterpret_cast<T*>(content)重解释类型转换;
它有着和C风格强制类型转换同样的功能;它可以转化任何的内置数据类型为其他的类型,
同时它也可以把任何类型的指针转化为其他的类型;它的机理是对二进制数据进行重新的
的解释,不会改变原来的格式,而static_cast会改变原来的格式;
2.c++中的一个空类,进行sizeof运算的结果是多少?c++中一个空类包含哪些成员?
C++中的一个空类,sizeof的运算结果是1.为什么不是0呢?空类型不包含任何的信息,
但是当我们声明该类型的实例的时候,它必须在内存中占用有一定的空间,否则无法
使用这些实例,至于占用多少的内存,由编译器决定一般是1.
一个空类一般包括如下的成员函数?
默认的缺省构造函数 Empty(){}
默认的析构函数 ~Empty(){}
拷贝构造函数 Empty(const Empty& empty){}
赋值运算符函数 Empty& operator=(const Empty& empty){}
取址运算符函数 Empty* operator&(){}
const属性的取址运算符函数 const Empty* operator&() const{}
3.构造函数,缺省构造函数;拷贝构造函数,缺省的拷贝构造函数;析构函数,缺省的析构函数;之间的区别?
1)构造函数存在的必要性: 因为C++程序设计希望像使用基本的数据类型那样使用类;而一般的时候
类的数据成员都是私有的,在外部不能访问;所以它要定义一个构造函数来确保它实例化的对象进行
初始化.确保一个类的不同对象之间具有自己想要的初始值.
2)什么时候会调用构造函数?
创建对象的时候会被自动调用,且仅被调用一次.对象定义语句,new操作符.
作用:为成员变量赋初值,分配资源,设置对象的初始状态;
对象的创建过程:
step1:为整个对象分配内存空间
step2:以构造实参调用构造函数包括下面活动
构造基类部分(先构造基类的部分)
构造成员变量(如果有类类型成员变量的时候会先去执行类类型的构造函数)
执行构造代码(这个是自己写的构造函数)
3)缺省构造函数都做什么事情?什么时候才会使用缺省构造函数?
当我们没有提供任何的构造函数的时候,系统会提供一个缺省构造函数.
缺省的构造函数做的事情:
a.对基本的成员变量,不做初始化.
b.对类类型的成员变量和基类子对象,调用相应类型的缺省构造函数初始化
c.对于已经定义至少一个构造函数的类,无论其构造函数是否带有参数,编译器都
不会为其提供缺省构造函数;
缺省的构造函数也叫无参构造函数,但是未必真的没有任何参数,为一个有参构造函数
的每个参数提供一个缺省值,同样可以达到无参构造函数的效果;
4)什么是拷贝构造函数?默认的缺省拷贝构造函数都做什么事情?拷贝构造函数的时机?
a.对于普通的内置类型进行复制的时候,是很简单的.例如int a = 4;int b = a;
由于用户自己定义的类类型复制的时候,就要调用拷贝构造函数.它的参数必须是
const 类名& that的构造函数,用于从一个已经定义的对象构造其同类型的副本,即
对象的克隆;
b.对于缺省的拷贝构造函数它所做的就是浅拷贝;(一般没有指针成员的时候可以使用)
它主要做的事情就是对基本的成员变量,它会逐字节的复制;
对类类型成员变量和基类子对象,调用相应类型的拷贝构造函数.
c.拷贝构造函数的时机:
1.用已经定义的对象作为同类型对象的构造实参;(S s1 = s2这是一个特例.)
2.以对象的值的形式向函数传递参数.
3.从函数中返回一个对象的值.
4.某些拷贝构造过程会因编译优化而被省略.
5)什么是赋值运算符函数?什么时候会用到赋值运算符函数?赋值运算符编写的时候要要注意的事项?
a.A& operator=(const A& a);
b.当用一个已经存在的对象赋值给另外一个已经存在的对象时,会调用赋值运算符函数
c.缺省方式的拷贝构造和拷贝赋值,对包括指针在内的基本类型成员变量按字节赋值,导致
浅拷贝问题(只拷贝地址,没有拷贝内容);为了获得完整意义上的对象的副本,必须自己
定义拷贝构造函数和拷贝赋值,针对指针型成员变量做深拷贝;
d.相对于拷贝构造,拷贝赋值需要做更多的工作;
避免自赋值
分配新资源
拷贝新内容
释放旧资源
返回自引用
尽量复用拷贝构造函数和析构函数中的代码;
->拷贝构造:分配新资源,拷贝新内容;
->析构函数:释放旧资源;
6)拷贝构造和拷贝赋值总结;
a.无论是拷贝构造还是拷贝赋值,其缺省实现对类类型成员变量和基类子对象,
对会调用相应类型的拷贝构造函数和拷贝赋值运算符函数,而不是简单的按
字节复制,因此应当尽量避免使用指针型成员变量;
b.尽量通过引用或指针向函数传递对象型参数,既可以降低参数传递的开销,
也能减少拷贝构造的机会.
c.处于具体的原因的考虑,确实无法实现完整意义上的拷贝构造和拷贝赋值的时候,
将它们私有化,以防误用.
d.如果一个类提供了自定义的拷贝构造函数,就没有理由不提供实现相同逻辑
的拷贝赋值运算符函数.
7)析构函数;
析构函数每个类成员只有一个,值在主函数执行完之后再执行的.析构函数
不能重载;
在销毁对象时自动被调用,且仅被调用一次;
对象离开所用域的时候会调用析构
delete操作符也会调用析构
释放在对象的构造过程或声明周期内获得的资源
如果一个类没有定义析构函数的时候,那么编译器会为其提供
一个缺省的析构函数;功能如下:
对基本类型的成员变量,什么也不做;
对类类型的成员变量和基类子对象,调用相应类型的析构函数;
析构函数的执行调用顺序:(对象的销毁过程)
a->执行析构代码;
b->析构成员变量
c->析构基类部分
d->释放整个对象所占用的内存空间
析构和构建的过程是完全相反的,构造的时候一定是先构造
基类的部分再来构造自己的部分;