1.(1)32位系统下任何类型指针的长度都是4个字节;
(2)指针的数据类型声明的是指针实际指向内容的数据类型;
(3)野指针是指向未分配或者已释放的内存地址;
(4)野指针,也就是指向不可用内存区域的指针。通常对这种指针进行操作的话,将会使程序发生不可预知的错误。
“野指针”不是NULL指针,是指向“垃圾”内存的指针。野指针的成因主要有两种:
1)、指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
2)、指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。别看free和delete的名字恶狠狠的(尤其是delete),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。通常会用语句if (p != NULL)进行防错处理。很遗憾,此时if语句起不到防错作用,因为即便p不是NULL指针,它也不指向合法的内存块。
free之后的指针仍然指向原来的堆地址,即仍然可以继续使用,但很危险。因为操作系统已经认为这块内存可以使用,它会毫不考虑的将他分配给其他程序,于是你下次不小心使用到该指针(野指针)时,如果操作系统及时制止了这种行为,报错(非法操作),然后将你的程序杀掉,给你很容易改正错误的机会,这还算比较好的结果!如果操作系统没有制止这种行为,那么产生的后果可就说不准了,说不定整个操作系统会崩溃,那么你再来改正这个错误,就不容易发现咯!所以,最好free了以后再置空,即令指针 = NULL;,表示本程序已经放弃再使用该指针。
2.类的大小只与成员变量(非static数据成员变量)和虚函数指针有关,还要考虑到对齐.
那么类A的大小等于4个字节 + 4个字节(考虑对齐) + 4个字节(指向虚函数的指针)=12字节;
类B的大小就是等于类A的大小12个字节.
因为在基类中存在虚函数时,派生类会继承基类的虚函数,因此派生类中不再增加虚函数的存储空间(因为所有的虚函数共享一块内存区域),而仅仅需要考虑派生类中添加进来的非static数据成员的内存空间大小。所以类B大小为12B
3. 派生类强制转化成基类(dynamic_cast)会做相应成员变量和函数的裁剪,仅能访问从基类继承来的部分成员;
4.一个C++程序是由一个或多个函数所组成,即使是最简单的程序,也必须有一个 main 函数。该函数是程序执行的起点和 终点 。C++中,函数不允许嵌套定义,允许嵌套调用。
5.在c++中,函数名实际上是一个指针,它指向函数入口。
6. 运行Test函数后会有“输出乱码”的结果。
char *getmemory(void) {
char p[]="helloworld";
return p;
}
void test(void) {
char *str=NULL;
str=getmemory();
printf(str);
}
分析:getmemory()返回的指针,是内部变量,调用之后会被回收。所以输出是不确定的。
假如修改为
char*getmemory(void)
{
char*p=”helloworld”;
returnp;
}
就可以输出"helloworld"啦!
返回“字符串常量的指针”和“返回数组名”的区别在于,一个返回静态数据区的地址,一个返回栈内存(动态数据区)的地址。
7.类的大小只与成员变量(非static数据成员变量)和虚函数指针有关,还要考虑对齐。虚函数有一个指向虚函数列表的指针,无论有多少个虚函数都是占用一个字节的大小。
#include<iostream>
#include<string.h>
using namespacestd;
class A {
public :
int fun1();
virtual void fun2();
private:
int _a;
};
int main() {
cout<<sizeof(A)<<endl;
}
分析:虚函数和private里的int各占4字节,一共8字节。类的函数是该类所有实例共享的,调用时通过隐藏的this指针和类的实例相关联。函数代码编译后根本就不在类实例中,所以不占实例空间
8.
#include<iostream>
#include<string.h>
using namespacestd;
void fun(char*p,int n) {
char b[6]="abcde";
int i;
for(i=0,p=b;i<n;i++)
p[i]=b[i];
}
int main() {
char a[6]="ABCDE";
fun(a,5);
printf("%s\n",a);
}
本题考查数组名作为函数参数,
执行f函数时,
传进去的a
指针被重新指向了b
,
所以原本a
数组的地址内容不变,所以输出结果为ABCDE
如果将上述代码的p=b;去掉的话,最终打印结果改变。
#include<iostream>
#include<string.h>
using namespacestd;
void fun(char*p,int n) {
char b[6]="abcde";
int i;
for(i=0,p=b;i<n;i++)
p[i]=b[i];
}
int main() {
char a[6]="ABCDE";
fun(a,5);
printf("%s\n",a);
}
分析:如果没有p=b;那么p就是指向a,p[i]=a[i]改变的就是a。最终打印结果为abcde
9. (1)所谓虚函数就是多态情况下只执行一个,而从继承的概念来讲,总是要先构造父类对象,然后才能是子类对象,如果构造函数设为虚函数,那么当你在构造父类的构造函数时就不得不显示的调用构造,还有一个原因就是为了防错,试想如果你在子类中一不小心重写了个跟父类构造函数一样的函数,那么你的父类的构造函数将被覆盖,也即不能完成父类的构造.就会出错. 在构造函数不要调用虚函数。
(2)在基类构造的时候,虚函数是非虚,不会走到派生类中,既是采用的静态绑定。显然的是:当我们构造一个子类的对象时,先调用基类的构造函数,构造子类中基类部分,子类还没有构造,还没有初始化,如果在基类的构造中调用虚函数,如果可以的话就是调用一个还没有被初始化的对象,那是很危险的,所以C++中是不可以在构造父类对象部分的时候调用子类的虚函数实现。但不是说你不可以那么写程序,你这么写,编译器也不会报错。只是你如果这么写的话编译器不会给你调用子类的实现,而是还是调用基类的实现。
(3)在析构函数中也不要调用虚函数。在析构的时候会首先调用子类的析构函数,析构掉对象中的子类部分,然后在调用基类的析构函数析构基类部分,如果在基类的析构函数里面调用虚函数,会导致其调用已经析构了的子类对象里面的函数,这是非常危险的。
构造函数不能声明为虚函数的原因是:
1)构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的。而在构造一个对象时,由于对象还未构造成功。编译器无法知道对象的实际类型,是该类本身,还是该类的一个派生类,或是更深层次的派生类。无法确定。
2)虚函数的执行依赖于虚函数表。而虚函数表在构造函数中进行初始化工作,即初始化vptr,让他指向正确的虚函数表。而在构造对象期间,虚函数表还没有被初始化,将无法进行。
先析构子类再析构父类,如果父类析构函数有虚函数,会导致调用子类的已经析构的内容。
先构造父亲类再构造子类,如果父类构造函数有虚函数,会导致调用子类还没构造的内容。