1、以下代码会有什么问题?
cout << (true?1:"1") << endl;
答:操作数的类型不兼容, int 和 const char*
2、以下代码输出什么?
char str1[] = "abc";
char str2[] = "abc";
const char str3[] = "abc";
const char str4[] = "abc";
const char* str5 = "abc";
const char* str6 = "abc";
cout << boolalpha << ( str1==str2 ) << endl; // 输出false
cout << boolalpha << ( str3==str4 ) << endl; // 输出false
cout << boolalpha << ( str5==str6 ) << endl; // 输出true
答案:数组名代表的是指针地址,而用const修饰只是代表变量内容能否被改变而已。所以第一行和第二行都是输出0。str5和str6是常量指针,指向同一个内存块。
3、以下代码可以输出0吗?
struct CLS
{
int m_i;
CLS(int i) : m_i(i) {}
CLS()
{
CLS(0);
}
};
CLS obj;
cout << obj.m_i << endl;
答:不能输出0。默认构造函数里面执行的带参数的构造函数其实是创建了一个新的对象,元对象的成员变量的值没有做修改。代码做如下修改可以输出0:
struct CLS
{
int m_i;
CLS(int i) : m_i(i) {}
CLS()
{
*this = CLS(0);
}
};
4、c++中的空类默认产生哪些类成员函数?
答:默认产生 构造函数、拷贝构造函数、析构函数、赋值运算符、取址运算符、常量取址运算符。
class Empty
{
public:
Empty(); // 缺省构造函数
Empty(const Empty&); // 拷贝构造函数
~Empty(); // 析构函数
Empty& perator=(const Empty&); // 赋值运算符
Empty* operator&(); // 取值运算符
const Empty* operator&() const; // 取值运算符
};
5、下面代码输出什么?
float a = 1.0f;
cout < < (int)a < < endl; //输出1
cout < < (int&)a < < endl; //输出1065353216
cout < < boolalpha < < ( (int)a == (int&)a ) < < endl; // 输出 false
//因为float 1 和 int 1 在内存中的表示方法不一样。
float b = 0.0f;
cout < < (int)b < < endl; //输出0
cout < < (int&)b < < endl; //输出0
cout < < boolalpha < < ( (int)b == (int&)b ) < < endl; // 输出true
//float 0 和 int 0 在内存中表示的方法是一样的。
解释:float b = 0.0f;
(int)b //把数据强转为int型,一般只会丢失小数部分
(int)&b //把b的地址转为int数
(int&)b 把b处的内存bit按int型数据解释.
6、写一个函数完成内存之间的拷贝
void* mymemcpy(void *dest, const void *src, size_t count)
{
char* pdest = static_cast<char*>(dest);
const char* psrc = static_cast<const char*>(src);
if (pdest > psrc && pdest < (char*)psrc + count ) //能考虑到这种情况就行了
{
for (size_t i = count - 1; i != -1; --i)
pdest[i] = psrc[i];
}
else
{
for (size_t i = 0; i < count; ++i)
pdest[i] = psrc[i];
}
return dest;
}
7、main函数执行完毕后是否可能会再执行其他代码。
答案:使用atexit或者_onexit注册函数。
atexit是标准C中的函数,建议使用这个。
onexit是非标准的。有些平台上不存在这个函数。
8、多态类中的虚函数表是Compile-Time,还是Run-Time时建立的?
答:虚拟函数表是在编译期就建立了,各个虚拟函数这时被组织成了一个虚拟函数的入口地址的数组.而对象的隐藏成员--虚拟函数表指针是在运行期--也就是构造函数被调用时进行初始化的,这是实现多态的关键.
9、一个父类写了一个virtual 函数,如果子类覆盖它的函数不加virtual ,也能实现多态?在子类的空间里,有没有父类的这个函数,或者父类的私有变量?
答案:只要基类在定义成员函数时已经声明了virtual关键字,在派生类实现的时候覆盖该函数时,virtual关键字可加可不加,virtual修饰符会被隐形继承的,不影响多态的实现。子类的空间里有父类的所有变量(static除外)
10、用代码实现判断一个int类型的数是否是2的幂,不可用用递归。
bool IsTwoPower(int s)
{
return (s > 0) && ((s & (s-1))==0);
}
11、在C++ 程序中调用被C编译器编译后的函数,为什么要加extern “C”声明?
答:C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为: void foo(int x, int y);该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。C++提供了C连接交换指定符号extern“C”来解决名字匹配问题。
12、用变量a给出如下定义:
int a; //一个整型数
int *a; //一个指向整型数的指针
int **a; //一个指向指针的指针,它指向的指针是一个整型。
int a[10]; //一个有10个整型数的数组
int *a[10]; //一个有十个指针的数组,该指针是指向一个整型数。
(int*)a[10]; //一个指向有10整型数数组的指针。
int (*a)(int); //一个指向函数的指针,该函数有一个整型参数,并返回一个整型值。
int (*a[10])(int); //一个有10个指针的数组,该指针指向一个函数,并返回一个整型数。
12、关键字static的作用
- 1、在函数体内,一个呗声明为static的变量在这一函数调用过程中维持值不变(只初始化一次)。
- 2、在模块内,一个被声明为static的变量可以被模块内所有的函数访问,但是不能被模块外的函数访问。是一个本地的全局变量。
- 3、在模块内,一个被声明为static的函数可以被这一模块内的其他函数调用。那就是这个函数被限制在声明它的模块的本地范围内使用。
- 4、未经初始化的静态全局变量会被程序自动初始化为0
在C++中的类成员前声明static: - 类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致 了它仅能访问类的静态数据和静态成员函数。
- 不能将静态成员函数定义为虚函数。
- 静态数据成员是静态存储的,所以必须对它进行初始化
13、const关键字的作用?
用const修饰意味着是只读的。
const int a; //a是一个常整型数
int const a; //同上
const int *a; //指向一个常整型数的指针(整型数不可修改,指针可修改)
int * const a; //指向一个整型数的常量指针(指针指向的值可以修改,但是指针本身不可修改)。
int const *a const; //指向常整数的常指针。(值和指针都不可修改)
//技巧:const在谁后面谁就不可修改,const在最前面则将其后移一位即可,二者等效
const有以下作用:
- 欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了。
- 对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const
- 在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值
- 对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量。const 在函数名后。 比如: void func() const
- 对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”.
const 在函数名前 const int func()。
14、指针与数组的区别:
访问指针时,先要找到指针变量本身的地址,从该地址再取到存放的指针值,然后对指针指向的对象进行访问,是间接访问。访问数组则是先找到数组变量符号代表的地址,对这个地址指向的对象进行访问,是直接访问。
数组要么存放在静态存储区或者栈上。数组名对应着一块内存,其地址与容量在生命周期内保持不变。只有数组的内容可以改变。当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。
指针可以随时指向任意的内存块,特征是可变的。
15、如何判断一个操作系统是16位还是32位,不能用sizeof()
int a = ~0;
if(a > 65535)
{
cout<<"32"<<endl;
}else{
cout<<"16"<<endl;
}
16、C和C++有什么不同?
从机制上看:C是面向过程的,C++是面向对象的,提供了类。
17、进程间通讯有哪些方式?
共享内存、管道、socker、消息队列。
18、new 、delete 、malloc、free关系。
delete会调用对象的析构函数,而free只会释放内存。new会调用构造函数。malloc和free是C/C++的标准库函数,而new和delete是C++的运算符。它们都可用于申请动态内存和释放内存。对于非内部数据类型的对象而言,光用malloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。
19、delete和delete []的区别
delete只会调用一次析构函数,而delete[]会调用每个成员的析构函数。delete与new配套,delete []与new []配套。
20、子类析构时要调用父类的析构函数吗?
析构函数调用的次序是先派生类的析构后基类的析构,也就是说在基类的的析构调用的时候,派生类的信息已经全部销毁了。定义一个对象时先调用基类的构造函数、然后调用派生类的构造函数;析构的时候恰好相反:先调用派生类的析构函数、然后调用基类的析构函数。
21、多态、虚函数、纯虚函数
多态是对于不同对象接收相同消息时产生不同的动作。C++的多态性具体体现在运行时和编译时两个方面。
运行时的多态是通过虚函数与继承来实现。
编译时的多态体现在函数和运算符的重载。
虚函数:在基类中冠以关键字 virtual 的成员函数。 它提供了一种接口界面。允许在派生类中对基类的虚函数重新定义。
纯虚函数的作用:在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义。作为接口而存在 纯虚函数不具备函数的功能,一般不能直接被调用。从基类继承来的纯虚函数,在派生类中仍是虚函数。如果一个类中至少有一个纯虚函数,那么这个类被称为抽象类。抽象类中不仅包括纯虚函数,也可包括虚函数。抽象类必须用作派生其他类的基类,而不能用于直接创建对象实例。但仍可使用指向抽象类的指针支持运行时多态性。
22、什么是“引用”?声明和使用“引用”要注意哪些问题?
引用就是某个目标变量的“别名”(alias),对应用的操作与对变量直接操作效果完全相同。声明一个引用的时候,切记要对其进行初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。
23、将“引用”作为函数参数有哪些特点?
- 传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
- 使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
- 使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
24、在什么时候需要使用“常引用”?
如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。
25、将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?
格式:类型标识符 &函数名(形参列表及类型说明){ //函数体 }
好处:在内存中不产生被返回值的副本;
注意事项:
- 不能返回局部变量的引用。
- 不能返回函数内部new分配的内存的引用。
- 可以返回类成员的引用,但最好是const。
26、结构与联合有和区别?
结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)
对于联合体的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构体的不同成员赋值是互不影响的
27、重载(overload)和重写(overried,有的书也叫做“覆盖”)的区别?
重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
重写:是指子类重新定义父类虚函数的方法。
28、哪几种情况下只能用初始化列表,而不能用赋值?
当类中含有const、reference 成员变量;基类的构造函数都需要初始化表。
29、描述内存分配方式以及它们的区别?
- 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
- 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
- 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。
30、请说出const与#define 相比,有何优点?
const作用:定义常量、修饰函数参数、修饰函数返回值三个作用。被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。
31、引用与指针有什么区别?
- 引用必须被初始化,指针不必。
- 引用初始化以后不能被改变,指针可以改变所指的对象。
- 不存在指向空值的引用,但是存在指向空值的指针。
32、基类的析构函数不是虚函数,会带来什么问题?
在实现多态时,当用基类操作派生类,在析构时防止只析构基类而不析构派生类的状况发生。避免内存泄漏。
33、宏定义中的#、##、#@
在一个预处理器宏中的参数前面使用一个#,预处理器会把这个参数转换为一个字符数组(字符串)。