与我前面写的C语言进阶知识点遥相呼应。
这才是C++面试,网上的面试题有些太简单了。
C++面试题目最多集中在对象的内存模型,记住了:如果用c/c++,内存都不清楚,还写个屁的程序!
1.C++的虚函数是怎样实现的?
C++的虚函数使用了一个虚函数表来存放了每一个虚函数的入口地址,这个虚函数表又使用一个虚函数指针来进行访问。通常,虚函数指针都放在对象模型的第一个位置存放,这样访问徐函数指针的速度最快,没有偏移量。通过虚函数指针,找到虚函数表,进而再做一个次偏移量计算,来得到真实虚函数的入口地址,从而访问了虚函数。这样看来,访问一个虚函数将使用两次间接计算,故要失去一些时间效率。另外,使用了虚函数,那么就要消耗一些空间,存放虚函数指针。但是,这都是值得的,为了实现多态。
2. 虚函数表是每个对象一个还是每个类一个呢?
这个事搜狗的面试题目。其实讨论过,我之前看到这个问题,立马就想到了this指针。http://topic.csdn.net/u/20081031/16/6784c211-a475-4af5-a331-c3df887a2d0c.html比较详细的讨论。
每个类只有若干个虚拟函数表,每个对象可以有若干个虚函数表指针。(这都是在多重继承的情况下出现的。单重继承的情况下,就是一个对象有一个虚函数表指针,每一个类有一个虚函数表)函数调用的时候会将this指针作为参数发过去,所以没有必要为每一个对象做一个虚拟函数表。
“每个类有一个或多个虚函数表,单纯的继承下来的类有一个虚函数表,虚继承下来的类就有可能有多个虚函数表(只是有可能),对象没有虚函数表,但是有指向虚函数表的指针。对象之所一能调用“自己的函数”,是因为类的成员函数不知不觉的在编译时多了一个this指针参数来识别成员数据属于哪个对象。”
关于这个问题,在《more effective c++》中的条款24有详细叙述,的确是每一个类的一个虚函数表。至于放的的位置,书中也有讲述。
3.函数指针和指针函数的区别?
就想通过这个问题来判断我的C水平?C语言我看的数都有三本。
函数指针,一个指针,指向了函数的调用地址。 void (*fun)(void)
指针函数,就是一个函数的返回值是指针。 int * fun(void)
这道题目我真的很冤,指针函数,我真的没有听说过。
数组指针和指针数组我知道什么区别
4. C++ 深拷贝和浅拷贝的区别?如果要delete一个浅拷贝,需要注意什么条件?
现在有一个指针p1指向了一个内存空间m1;
浅拷贝就是再用一个新的指针p2指向这片内存空间m1;
深拷贝就是用一个新的指针p3指向m1的副本m2
delete一个浅拷贝,首先要测试是不是有其它的指针还在指向这片空间。不然,直接就是野指针了。为什么野指针那么是绝对要禁止的?野指针现在指向了一片内存区间,这片内存区间以前是有意义的,现在被释放了,操作系统可能会讲这边区间放上其它程序数据。那么,野指针仍然指向了这片区间。如果此时使用野指针,改变了这片内存的数据,那么程序应该直接崩溃。而且,这样子的崩溃可能出现,可能等一会儿出现,不定。带来的bug很难查找。
5. dynamic_cast的用法?
http://blog.csdn.net/wingfiring/article/details/633033
作为四个内部类型转换操作符之一的dynamic_cast和传统的C风格的强制类型转换有着巨大的差别。除了dynamic_cast以外的转换,其行为的都是在编译期就得以确定的,转换是否成功,并不依赖被转换的对象。而dynamic_cast则不然。在这里,不再讨论其他三种转换和C风格的转换。
首先,dynamic_cast依赖于RTTI信息,其次,在转换时,dynamic_cast会检查转换的source对象是否真的可以转换成target类型,这种检查不是语法上的,而是真实情况的检查。
先看RTTI相关部分,通常,许多编译器都是通过vtable找到对象的RTTI信息的,这也就意味着,如果基类没有虚方法,也就无法判断一个基类指针变量所指对象的真实类型, 这时候,dynamic_cast只能用来做安全的转换,例如从派生类指针转换成基类指针.而这种转换其实并不需要dynamic_cast参与.
也就是说,dynamic_cast是根据RTTI记载的信息来判断类型转换是否合法的。
struct B1{ virtual ~B1(){} }; struct B2{ virtual ~B2(){} }; struct D1 : B1, B2{}; int main() { D1 d; B1* pb1 = &d; B2* pb2 = dynamic_cast<B2*>(pb1);//L1 B2* pb22 = static_cast<B2*>(pb1); //L2 return 0; }
http://baike.baidu.com/view/1745213.htmdynamic_cast < type-id > ( expression )
该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void*;
如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。 dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
http://blog.csdn.net/wingfiring/article/details/633033dynamic_cast 确实很好地解决了我们的问题,但也需要我们付出代价,那就是与 typeid 相比,dynamic_cast 不是一个常量时间的操作。为了确定是否能完成强制类型转换,dynamic_cast`必须在运行时进行一些转换细节操作。因此在使用 dynamic_cast 操作时,应该权衡对性能的影响
5. 请阅读以下的代码,试试看,你能发现错误或者不恰当地方吗?(提示: 不止一处)
http://topic.csdn.net/t/20061129/14/5194019.html
class Wheel{}; class Color{}; class Car { private: Wheel* wheels; public: Car(int wheel_count){wheels = new Wheel[wheel_count];} ~Car(){delete wheels;} }; class Roadster : public Car { public: Color color; Roadster(){}; Roadster(const Roadster& rs) { this-> color = rs.color; } ~Roadster(){}; Roadster& operator=(const Roadster& rhs) { this-> color = rhs.color; } const Roadster& clone() { Roadster the_new = *this; return the_new; } }; int main(int argc, char* argv[]){ Roadster* yours = new Roadster; Roadster mine; mine = yours-> clone(); Car* pCar = yours; delete pCar; return 0; };
修改如下:
class Wheel{}; class Color{}; class Car { private: Wheel* wheels; //1. 没有保存wheels的下标 //2. 最好用vector public: Car(int wheel_count){wheels = new Wheel[wheel_count];} ~Car(){delete wheels;} //3. 析构函数要虚拟 //4. delete wheels; -> delete []wheels; //5. 要提供拷贝构造函数 //6. 要重载赋值操作符 }; class Roadster : public Car { public: Color color; //7. 成员变量要私有 Roadster(){}; //8. 要调用父类的构造函数 Roadster(const Roadster& rs) { this-> color = rs.color; } //9. 要调用父类的拷贝构造函数 ~Roadster(){}; //10. 析构函数要虚拟(原则:析构函数要么虚拟且公有,要么保护且非虚拟) Roadster& operator=(const Roadster& rhs) { this-> color = rhs.color; } //11. 需要调用父类的赋值操作符 //12. 没有返回值 //13. 返回的类型最好是const Roadster&, 而不是Roadster& const Roadster& clone() { Roadster the_new = *this; return the_new; } //14. 不能返回局部变量的引用 //15. 按照这里的逻辑, 其实可以直接返回*this }; int main(int argc, char* argv[]){ //16. 这里的{}风格与前面的不一致 Roadster* yours = new Roadster; Roadster mine; mine = yours-> clone(); //17. 最好是一行只有一条语句, 不要两条语句放在一行 //18. 其实这里可以直接是Roadster mine = yours-> clone(); Car* pCar = yours; delete pCar; //我认为这里是没有问题的,作为Car类的使用者,他在这里用是没有问题的,错误的只是Car类的代码 return 0; };
6.C++程序进入main函数之前,退出main函数之后会做些什么?
main函数执行之前,主要就是初始化系统相关资源: