多态性有哪些?(静态和动态,分别叙述了一下虚函数和函数重载) { 分为静态多态性和动态多态性,静态就是在编译时就已经确定了,动态是在程序运行时 才能确定。像函数重载,就是多个函数具有相同的函数名,但是函数的参数类型, 个数,顺序必须有所不同。如果基类和派生类成员函数原型一致,则派生类的成员函数将覆盖基类的。 动态多态性的话像虚函数,在派生类中重新定义虚函数时,原型必须与基类一致。在程序运行 期间,使用一个基类指针动态地指向基类或派生类的对象,再调用虚函数。 } 动态绑定怎么实现?(基类与派生类指针和引用的转换问题) { 必须通过基类的引用或指针进行函数调用,还必须是虚函数。那么就会发生动态绑定。 } 类型转换有哪些?(四种类型转换) { 有static_cast,const_cast,dynamic_cast,reinterpret_cast四种, 那么static_cast实现不同数据类型之间的转换,如把int转换为float(写法是 f=static_cast<float>(i) ); const_cast是取消const属性,可以将const类型的指针变为非const类型的指针 dynamic_cast主要用于基类和派生类之间的转换,会检查操作是否有效,不成功的话,指针会返回null,引用 会抛出异常。 reinterpret_cast会对二进制数据进行重新的解释,可以进行无关类型之间的转换,但是很不安全。 } 操作符重载? { 要用到 operator 关键字,如 bool operator ==(参数),(如果重载操作符是类成员,则左操作数必须是该类 的对象,该操作符才会被调用,如果该操作符的左操作数是其他的类型,则需要被重载为全局名字空间的成员。) C++中 =(赋值运算符),[](下标运算符),()(函数调用运算符),和->(成员指向)必须被定义为类成员操作符。 像成员选择符(.)、域名解析 操作符(::)、条件操作符外(?:)不能被重载,重载操作符不能改变他们的操作符优先级, 不能改变操作数的个数。 (T& operator ++ ()//前置增量 T& operator ++ (int) //后置增量 ) } 内存对齐的原则? { (1)每一个数据成员的偏移量必须是自身长度的倍数,像int,则地址必须是4的倍数。 (2)结构本身也要对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行 } 模板怎么实现? { 格式就是 template<typename 形参名> 定义类模板如 template<class 形参名> ,创建对象时如A<int> a;在类模板外定义成员函数时, 类似void A<T>::func(){},参数可以是类型形参,非类型形参和模板形参,非模板类型的形参 只能是整型,指针和引用 } 指针和const的用法? { 最简单的指针如 int* p,说明p是指向一个int型数据的指针,还有指针数组,写法如 int *p[3], 代表数组里 的全是指针,还有数组指针,写法如 int (*p)[3],指针指向的内容是一个数组,还有函数指针,写法如 int (*p)(int); 代表指向的是有一个int型参数求且返回值是int的函数。还有二级指针。 const声明变量需要初始化,且不能被修改,如果修饰指针的话,主要有两种情况,如 const int* p这种所指内 容是常量,不可被修改,另外一种如 int *const p,则指针是常量,所指内容可修改。在类中使用的话,可以 修饰类的数据成员,不能在类中初始化,要在构造函数的初始化列表里初始化。const 可以修饰函数的参数, 返回值以及整个函数,参数可能是指针或引用,则加了const可以保护其内容,一般修饰函数的话const放在 函数体后,则函数不能修改数据成员或者调用其他非const成员函数。 } const 和 #define的区别? { 编译器处理方式不同,宏在预处理阶段替换,const在编译阶段替换。类型和安全检查不同,宏没有类型,不做安全 检查,const有类型,在编译阶段做安全检查。 } 虚函数、纯虚函数、析构函数? { 虚函数和纯虚函数都要加 virtual 修饰,虚函数可以直接被使用,也可以被子类重载以后以多态形式调用,而 纯虚函数在基类里只有声明没有定义,必须在子类中实现之后才能被使用。含有纯虚函数的类被称为抽象类, 纯虚函数后加 =0, 抽象类是不能被实例化的 析构函数在类名前加~, 当对象的生命周期结束时,会自动执行析构函数。一个类只能由一个析构函数,不能被 重载。static局部对象在函数调用结束时对象并不释放,只在main函数结束或调用exit函数结束程序时,才调用 static 局部对象的析构函数。全局对象同理,如果是用new 动态建立的,当用delete释放该对象时,先调用该对象 的析构函数。 } 内联函数(内联函数的优点以及和宏定义的区别) { 内联函数相当于自动地用函数的形式添加进代码,可以提高效率。编译器会对内联函数的参数做安全检查或自动类型转换, 而宏定义不会。内联函数可以访问类的私有成员变量,而宏定义则不能,在类中声明同时定义的成员函数,自动转化为 内联函数。内联函数中复杂的操作,如循环和递归调用将不被关联。 } typedef 的用法 { 它与#define有些类似,但有不同。typedef定义一种类型的别名,而不是简单的宏替换,可以帮助结构体定义别名, 用typedef来定义与平台无关的类型。比如定义一个REAL的浮点类型,如果在支持long double 的平台上,可以将long double 定义别名REAL,在不支持long double的平台上,再改成 typedef double REAL。 } c语言和c++有什么区别? { C是面向过程,C++是面向对象 最突出的部分是多了类的概念,由此衍生出封装,继承,重载,多态等。封装有点像结构体,但是结构体不能定义变量的访问 权限,结构体也不能被继承,C++可以实现运算符的重载。 C++引入了模板,使得C++可以实现泛型编程。
待补充。。。 } 二叉树的非递归遍历代码? { void PreOrderVisit(Node* root) { MyStack K; K.Init(); Node* p=root; while(p!=NULL||!K.Empty()) { while(p!=NULL) { Visit(p->data); K.Push(p); p=p->lchild; } if(!K.Empty()) { p=K.Pop(); p=p->rchild; } } } void InOrderVisit(Node* root) { MyStack K; K.Init(); Node* p=root; while(p!=NULL||!K.Empty()) { while(p!=NULL) { K.Push(p); p=p->lchild; } if(!K.Empty()) { p=K.Pop(); Visit(p->data); p=p->rchild; } } } void LastOrderVisit(Node* root)//设置一个标记,到第二次访问时才输出 { MyStack K; K.Init(); Node* p=root; Node* temp=NULL; while(p!=NULL||!K.Empty()) { while(p!=NULL) { p->IsFirst=true; K.Push(p); p=p->lchild; } if(!K.Empty()) { temp=K.Pop(); if(temp->IsFirst==true) { temp->IsFirst=false; K.Push(temp); p=temp->rchild; } else { Visit(temp->data); p=NULL; } } } } } 继承机制中对象之间是如何转换的? { 从派生类向基类的类型转换只对指针和引用类型有效。基类向派生类不存在隐式类型转换,使用dynamic_cast } 继承机制中引用和指针之间如何转换? { 基类向派生类转换,用dynamic_cast转换,首先检查基类指针是否真正指向一个派生类对象,再做相应处理,成功 返回派生类对象,失败则返回空指针。派生类向基类转换,可以用dynamic_cast或者直接进行类型转换。 } 虚函数、虚函数表里面内存如何分配? { 对于无虚函数覆盖的继承:虚函数按照其声明顺序放在虚函数表中;父类的虚函数在其子类的虚函数的前面。 对于 有虚函数覆盖的继承:派生类中起覆盖作用的虚函数放在原基类虚函数的位置;没有被覆盖的虚函数依旧。 对于无 虚函数覆盖的多重继承:每个父类都有自己的虚函数表;派生类的虚函数被放在了第一个父类的虚函数表中(按照声 明顺序排序)。 对于有虚函数覆盖的多重继承:派生类中起覆盖作用的虚函数放在原基类虚函数的位置;没有被覆 盖的虚函数依旧。 } 如何实现只能动态分配类对象、不能定义类对象? { 把类的构造函数和析构函数设为protected属性。类对象不能访问,但是派生类可以继承,也可以访问。 同时,创建create和destroy两个函数,用于创建类对象。 (create函数设为static,原因是,创建对象的时候A *p=A::create().只有静态成员函数才能有类名直接访问) } 红黑树的定义和解释? { 一棵红黑树是指一棵满足下述性质的二叉搜索树(BST, binary search tree): 1. 每个结点或者为黑色或者为红色。 2. 根结点为黑色。 3. 每个叶结点(实际上就是NULL指针)都是黑色的。 4. 如果一个结点是红色的,那么它的两个子节点都是黑色的(也就是说,不能有两个相邻的红色结点)。 5. 对于每个结点,从该结点到其所有子孙叶结点的路径中所包含的黑色结点数量必须相同。 红黑树,一种二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。通过对任何一条 从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。 前面说了,红黑树,是一种二叉查找树,既然是二叉查找树,那么它必满足二叉查找树的一般性质。 红黑树是通过节点分为红、黑两种颜色并根据一定的规则确保在一定程度上是平衡的,从而保证在红黑树中查找、 删除、插入操作都只需要O(logk)的时间。 } 静态成员函数和数据成员又什么意义? { A 声明为static的类成员或者成员函数便能在类的范围内共同享,类域中的全局变量。 B 派生类对象与基类对象共享基类的静态数据成员。 D 静态数据成员不能在类定义里边初始化,只能在class body外初始化。 E 静态成员函数没有this指针,它不能返回非静态成员,因为除了对象会调用它外,类本身也可以调用。静态 成员函数不可以调用类的非静态成员。因为静态成员函数不含this指针。 F 非静态成员函数可以任意地访问静态成员函数和静态数据成员。 } explicit是干什么用的? { 这个关键字的作用是将编译器隐式转换的功能给屏蔽掉。 } 模板特化的概念,为什么特化? { 通常又有一些特殊的情况,不能直接使用泛型模板展开实现,所以要用到模板特化 一是特化为绝对类型;比如特化为 double,float 等 template<> class Compare<float>{}; 二是特化为引用,指针类型;比如 const T*,const T& ; template<class T> class Compare<T*>{}; 三是特化为另外一个类模板。 template <class T1> struct SpecializedType { T1 x1; T1 x2; }; template <class T> class Compare<SpecializedType<T> >{}; 对于特殊类型, 比如说指针, 其拷贝就和基本数据类型不同. 这是为了处理这种特殊情况就需要类模板特化, 用于处理特定类型的特殊情况. } strcpy函数的编写? { char *strcpy(char *strDes,const char *strSrc) { assert(strSrc!=NULL); char *p=strDes; while(*strSrc!='