您的位置:首页 > 编程语言 > C语言/C++

C++中必须知道的问题

2014-01-03 15:41 309 查看
把前段时间学习c++中遇到的一些问题跟大家分享一下

1.静态成员函数为什么不能声明为const

2.当一个父类对象以其子类对象初始化时会发生什么

3.一个类的对象所占内存是由什么决定的

4.构造函数初始化列表的作用

5.为什么最好不要在构造函数和析构函数中调用虚函数

6.虚函数表是怎么回事

7.虚函数指针vptr存在于一个对象的什么位置,其作用是什么

8.一个string对象的数据成员占多少内存

9.一个空类的对象占几个字节

10.通过指针存取一个对象的数据成员和通过对象直接存取其数据成员有什么区别

11.什么时候会调用拷贝构造函数,什么时候又会调用赋值运算符?

12.构造函数为什么不能声明为虚函数

13. 类成员函数的重载、覆盖和隐藏区别

答案:
1.普通成员函数声明为const是防止函数修改调用该成员函数的对象的数据成员,这个对象时通过this指针隐式传递给该函数的
,而静态成员函数没有this指针,所以就没有这项功能,也就不需要声明为const

2.子类对象的数据成员包含继承自父类的数据成员和子类本省的数据成员两部分,所以用子类对象赋值给父类对象时会发生切割行为,即初始化的父类只会存在子类对象的父类数据成员部分

3.一个类的对象所占内存是由其数据成员(包括继承自父类的数据成员和自己本身的数据成员两部分)加上其虚函数指针(如果存在虚函数)

4.使用初始化列表的构造函数显式的初始化类的成员;而没使用初始化列表的构造函数是对类的成员赋值,并没有进行显式的初始化

5. 实例化一个派生类对象时,首先调用父类构造函数,此时派生类部分的成员变量还没有初始化,如果虚函数下降到派生类级,会发生不可预知的行为

6.C++中当一个类中存在virtual函数(虚函数)或者它的父类中存在虚函数,那么编译器就会为这个类生成虚函数表(virtual table),虚函数表中存放着虚函数的地址,含有虚函数的类或者其子类的对象编译器都为其加上一个虚函数指针vptr,vptr在执行时指向对应的虚函数,从而实现多态

7.vptr一般的编译器会将其放在对象所占内存空间的最前面的部分,vptr的作用就是指向虚函数表存放的函数
8.要解答这个问题就要高清string类的实现,一个string对象的数据成员包括存放该string的地址的指针,还有该string的长度两部分,即:4+strlen(str)+1

9.一个空类为1个字节,可能会因为编译器不同而有所不同,这一个字节是为了定位这个空类的对象,反过来思考会比较容易理解,我们假设空类对象不占内存,那么系统怎么区分这个空类的若干个对象呢?因为普通类(非空类)的对象都占据一定的内存空间,所以系统可以唯一的确定他们并调用他们相应的成员函数,那么为什么是1个字节,而不是2个,3个或者更多呢,当然是1个字节就可以定位空类的对象了,多的话岂不是浪费

10.当要存取的数据成员是从虚基类继承过来时,通过指针不能确定这个指针具体指向哪个对象,存取只能延迟到执行期间,而通过对象存取,在编译期间就已经确定了该数据成员相对于该对象的便宜,所以用对象存取效率更高,如果不存在虚基类,那么两种方式完全一样
11.在一个新对象被创建时用另一个对象初始化该对象一定会调用拷贝构造函数,因为新对象的创建一定会调用构造函数,如果不存在新对象的创建那么就会调用赋值运算符

12.构造函数是分配内存的行为,虚函数需要通过虚函数表虚函数指针来调用,在构造函数之前,这些都还没有初始化,所以也就无从调用,所以构造函数不能为虚函数

13. 成员函数被重载的特征:

(1)相同的范围(在同一个类中);

(2)函数名字相同;

(3)参数不同;

(4)virtual 关键字可有可无。

覆盖是指派生类函数覆盖基类函数,特征是:

(1)不同的范围(分别位于派生类与基类);

(2)函数名字相同;

(3)参数相同;

(4)基类函数必须有virtual 关键字。

“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:

(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。

(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)

对于有的网友的评论说第5题的答案有些问题,所以我在这里对第5题(为什么不要在构造函数和析构函数中调用希函数)给出了代码演示,希望您能通过这些断码能看出某些端倪:

下面的这段代码是用来说明为什么不要在构造函数中调用虚函数,注意是调用,就是函数调用

#include <iostream>

using namespace std;

class A

{

public:

A()

{

cout<<"A的构造函数"<<endl;

print();

}

virtual void print()

{

cout<<"A中的虚函数print()"<<endl;

}

virtual ~A()

{

cout<<"A的析构函数"<<endl;

}

};

class B:public A

{

public:

B()

{

cout<<"B的构造函数"<<endl;

print();

}

void print()

{

cout<<"B中的虚函数print()"<<endl;

}

virtual ~B()

{

cout<<"B的析构函数"<<endl;

}

};

int main()

{

 cout<<"************************"<<endl;

 A *pA = new B;

 pA->print();

 cout<<"************************"<<endl;

 delete pA;

 return 0;

}

下面贴出这段代码的输出,用的是微软的vc6.0,有兴趣的同学也可以在别的编译器如gcc下试试,我想答案应该也是一样的



看到程序的第二行输出了吧,我们在A的构造函数中调用了虚函数数print(),如果虚函数起作用那么应该调用B中的print(),输出“B中的虚函数print()”,原因就是new B首先会调用A的构造函数,A的构造函数执行时,B中的成员和vptr都还没有初始化,所以这时调用虚函数print()还调用的A自己的print()

再来看看为什么不要在析构函数中调用虚函数,这也是fera的问题,我在第5题的答案中确实没有回答,那是因为我觉得这个跟构造函数中为什么不要调用虚函数类似,既然有问题,那么下面给出代码演示:

#include <iostream>

using namespace std;

class A

{

public:

A()

{

cout<<"A的构造函数"<<endl;

print();

}

virtual void print()

{

cout<<"A中的虚函数print()"<<endl;

}

virtual ~A()

{

cout<<"A的析构函数"<<endl;

print();

}

};

class B:public A

{

public:

B()

{

cout<<"B的构造函数"<<endl;

print();

}

void print()

{

cout<<"B中的虚函数print()"<<endl;

}

/* virtual ~B()

{

cout<<"B的析构函数"<<endl;

print();

}

*/

};

int main()

{

 cout<<"************************"<<endl;

 A *pA = new B;

 cout<<"************************"<<endl;

 delete pA;

 return 0;

}

下面贴上这段断码的输出,有图有真相:



当delete pA执行时,先调用子类B的析构函数,这里避免混淆我注释了B的析构函数,然后再调用A的析构函数,如果A中的虚函数生效,那么肯定会调用B中的print()函数,因为A *pA = new B;显然根据输出的结果可以看出这里只调用了A中的虚函数print(),也就是说虚函数此时失效,因为如果虚函数起作用,那么会调用B的print()函数,而此时B的析构函数已经执行完毕,B中的成员已经被析构,所以不可能再调用他的任何函数,这就是虚函数在析构函数中失效的根本原因。

阅读(1230) | 评论(9) | 转发(6) |

0
上一篇:Linux缓冲输出问题

下一篇:python中matplotlib绘图中文显示问题

相关热门文章
高姿乐茉全面解决肌肤干燥问题...

高姿致力于打造中国专业美白第...

automake,autoconf使用详解...

24om创业路上

A5营销团队告诉我的SEO诊断那...

test123

编写安全代码——小心有符号数...

使用openssl api进行加密解密...

一段自己打印自己的c程序...

sql relay的c++接口

socket编程开启混杂模式的目的...

C++ 嵌套类是干什么的?...

C++ 单例怎么写?

sizeof 这个操作符都有什么特...

Oracle VM server 如何删除vm ...

给主人留下些什么吧!~~





fuliangcheng2013-03-12 20:16:50
fera:看了你的例子,觉得咱俩说的是同一个事,但出发点不一样。你说“析构不能调虚函数”,从语义上来说,因为虚函数机制不起作用,得不到想要的效果。而我是从语法角度来说“析构可以调用虚函数”,因为我不管调用虚函数时虚机制是否起作用。

“类型是确定的”:C继承B,B继承A,那么pA = new C(); ... delete pA;此时析构分三个阶段,析构~C(),~B(),~A(),在每个析构阶段,类型都是确定的,分别是C、B、A,当类型确定时,虚机制就失效。
可能是误说了,不是不能调用,应该是避免在构造函数和析构函数中调用虚函数,这样没有意义,谢谢您回复 | 举报





fera2013-03-12 13:44:54
fuliangcheng:前半部分正确,后半部分”此时的类型是确定的“ 我没有太明白,什么类型是确定的
看了你的例子,觉得咱俩说的是同一个事,但出发点不一样。你说“析构不能调虚函数”,从语义上来说,因为虚函数机制不起作用,得不到想要的效果。而我是从语法角度来说“析构可以调用虚函数”,因为我不管调用虚函数时虚机制是否起作用。

“类型是确定的”:C继承B,B继承A,那么pA = new C(); ... delete pA;此时析构分三个阶段,析构~C(),~B(),~A(),在每个析构阶段,类型都是确定的,分别是C、B、A,当类型确定时,虚机制就失效。回复 | 举报





fuliangcheng2013-03-12 13:35:35
fera:这么说吧,析构时虚函数机制基本上是不起作用的,因为此时的类型是确定的
前半部分正确,后半部分”此时的类型是确定的“ 我没有太明白,什么类型是确定的回复 | 举报





fera2013-03-12 12:45:05
fuliangcheng:“析构函数要调用类层次中上一层的析构,而那个析构也是虚函数。”你的意思是说子类的析构函数是虚函数,父类的析构函数也是虚函数,析构从子类开始,然后析构父类成员,这个没错,这个跟我说的不能在析构函数中调用虚函数没有什么冲突,稍后我会给出具体的代码演示
这么说吧,析构时虚函数机制基本上是不起作用的,因为此时的类型是确定的回复 | 举报





fuliangcheng2013-03-12 12:19:26
fera:析构函数要调用类层次中上一层的析构,而那个析构也是虚函数。而且你第5题的解答也没有回答5中第二个问题
“析构函数要调用类层次中上一层的析构,而那个析构也是虚函数。”你的意思是说子类的析构函数是虚函数,父类的析构函数也是虚函数,析构从子类开始,然后析构父类成员,这个没错,这个跟我说的不能在析构函数中调用虚函数没有什么冲突,稍后我会给出具体的代码演示回复 | 举报

首页

上一页

1

2

下一页

末页

评论热议
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: