面试题集锦(一)
class A { public: A ():m_iVal(0){test();} virtual void func() { std::cout<<m_iVal<<‘ ’;} void test(){func();} public: int m_iVal; }; class B : public A { public: B(){test();} virtual void func() { ++m_iVal; std::cout<<m_iVal<<‘ ’; } }; int main(int argc ,char* argv[]) { A*p = new B; p->test(); return 0; }
C++继承体系中,初始化时构造函数的调用顺序如下
(1)任何虚拟基类的构造函数按照他们被继承的顺序构造
(2)任何非虚拟基类的构造函数按照他们被继承的顺序构造
(3)任何成员对象的函数按照他们声明的顺序构造
(4)类自己的构造函数
据此可知 A*p = newB;先调用A类的构造函数再调用B类的构造函数。
构造函数中调用虚函数,虚函数表现为该类中虚函数的行为,即在父类构造函数中调用虚函数,虚函数的表现就是父类定义的函数的表现。why?原因如下:
假设构造函数中调用虚函数,表现为普通的虚函数调用行为,即虚函数会表现为相应的子类函数行为,并且假设子类存在一个成员变量int a;子类定义的虚函数的新的行为会操作a变量,在子类初始化时根据构造函数调用顺序会首先调用父类构造函数,那么虚函数回去操作a,而因为a是子类成员变量,这时a尚未初始化,这是一种危险的行为,作为一种明智的选择应该禁止这种行为。所以虚函数会被解释到基类而不是子类。在构造或析构函数中调用虚函数会执行与之所属类型相对应的虚函数版本。
class Base { public: Base(int j): i(j) {} virtual~Base() {} void func1() { i *= 10; func2(); } int getValue() { return i; } protected: virtual void func2() { i++; } protected: int i; }; class Child: public Base { public: Child(int j): Base(j) {} void func1() { i *= 100; func2(); } protected: void func2() { i += 2; } }; int main() { Base * pb = new Child(1); pb->func1(); cout << pb->getValue() << endl; delete pb; }
基类的指针指向不同的派生类的对象时, 基类指针调用其虚成员函数,则会调用其真正指向对象的成员函数, 而不是基类中定义的成员函数(只要派生类改写了该成员函数)。 若不是虚函数,则不管基类指针指向的哪个派生类对象,调用时都 会调用基类中定义的那个函数。
func1()不是虚函数,静态绑定,调用Base::func1();
func2()是虚函数,动态绑定,调用Child::func2();
union Test { char a[4]; short b; }; Test test; test.a[0]=256; test.a[1]=255; test.a[2]=254; test.a[3]=253; printf("%d\n",test.b);
char类型的取值范围是-128~127,unsigned char的取值范围是0~256
这里a[0]=256,出现了正溢出,将其转换到取值范围内就是0,即a[0]=0;
同理,a[1]=-1, a[2]=-2, a[3]=-3,在C语言标准里面,用补码表示有符号数,故其在计算机中的表示形式如下:
a[0]=0, 0000 0000
a[1]=-1, 1111 1111
a[2]=-2, 1111 1110
a[3]=-3, 1111 1101
short是2字节(a[0]和a[1]),由于80X86是小端模式,即数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中,在本例中,a[0]中存放的是b的低位,a[1]中存放的是b的高位,即b的二进制表示是:1111 1111 0000 0000,最高位是1是一个负数,在计算机中采用补码表示,那么二进制表示为:1000 0001 0000 0000,转化为十进制就是-256.
void getmemory(char*p) { p=(char *) malloc(100); strcpy(p,"hello world"); } int main( ) { char *str=NULL; getmemory(str); printf("%s\n",str); free(str); return 0; }
free(ptr)
如果ptr为NULL,则不会做任何操作
如果ptr未定义,则会崩溃。
/*C/C++中默认的类型转换*/
在c++中为了尽量保证精度不丢失,一般会把低转化为高精度,比如char->int->float->double
(I)i==(int)(float)I 由于i(int)经过强制类型转换从int->float->int和左边相同 正确
(II)f==(float)(int)f 由于f(float)经过强制类型转换 从float->int,虽然int也强制类型转换了但是小数点已经去掉,故精度丢失,和左边不一样,错误
(III)f==(float)(double) f 由于f(float)经过强制类型转换 从float->double->float和左边相同 正确
(IV)(d+f)-d==f 左边为了尽量保证精度不丢失,一般会把低转化为高精度从float->double 和右边float不同 错误
/*重载、覆盖、隐藏*/
a. 成员函数被重载的特征:
( 1 )相同的范围(在同一个类中);
( 2 )函数名字相同;
( 3 )参数不同;
( 4 ) virtual 关键字可有可无。
b. 覆盖是指派生类函数覆盖基类函数,特征是:
( 1 )不同的范围(分别位于派生类与基类);
( 2 )函数名字相同;
( 3 )参数相同;
( 4 )基类函数必须有 virtual 关键字。
c.“ 隐藏 ” 是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
( 1 )如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无 virtual 关键字,基类的函数将被隐藏(注意别与重载混淆)。
( 2 )如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有 virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)
typedef struct tagMystruct { int inum; long lLength; }Mystruct;
这语句实际上完成两个操作:
1) 定义一个新的结构类型
struct tagMystruct
{
int inum;
long lLength;
};
分析:tagMyStruct称为“tag”,即“标签”,实际上是一个临时名字,struct 关键字 和tagMyStruct一起,构成了这个结构类型,不论是否有typedef,这个结构都存在。
我们可以用struct tagMyStruct varName来定义变量,但要注意,使用tagMyStruct varName来定义变量是不对的,因为struct 和tagMyStruct合在一起才能表示一个结构类型。
2) typedef为这个新的结构起了一个名字,叫MyStruct。
typedef struct tagMyStruct MyStruct;
因此,MyStruct实际上相当于struct tagMyStruct,我们可以使用MyStruct varName来定义变量。
void swap_int(int *a,int *b){ *a=*a+*b; *b=*a-*b; *a=*a-*b; }
结果正确,即使会溢出
继承时:
1、不管采用哪种形式(public, protected或private),基类中的私有成员都不可以被继承;如果非要在派生类中使用基类的私有成员,可以有两种方法:一是使用属性,二是使用友元类或友元函数。
2、如果采用public形式,则基类除了私有成员的其它所有都被原样的继承到派生类中;即在基类中是public的,在派生类中还是public的,在基类中是protected的,在派生类中还是protected的。
3、如果采用protected形式,则基类中除了私有成员的其它说有都被以protected的形式继承到派生类中。
C++中的继承方式有:
public、private、protected三种(它们直接影响到派生类的成员、及其对象对基类成员访问的规则)。
(1)public(公有继承):继承时保持基类中各成员属性不变,并且基类中private成员被隐藏。派生类的成员只能访问基类中的public/protected成员,而不能访问private成员;派生类的对象只能访问基类中的public成员。
(2)private(私有继承):继承时基类中各成员属性均变为private,并且基类中private成员被隐藏。派生类的成员也只能访问基类中的public/protected成员,而不能访问private成员;派生类的对象不能访问基类中的任何的成员。
(3)protected(保护性继承):继承时基类中各成员属性均变为protected,并且基类中private成员被隐藏。派生类的成员只能访问基类中的public/protected成员,而不能访问private成员;派生类的对象不能访问基类中的任何的成员。