C/C++易错难点笔记01
2015-08-21 20:20
519 查看
疑惑点
C++是一门神奇的语言,很多时候你对底层不熟悉,很难知道某些情况下的结果,下面是我不断积累的疑惑点,这里将其记录下来。类的转换问题
代码:[code]class A{ public: virtual void f() { cout << "A" << endl; } }; class B: public A{ public: virtual void f() { cout << "B" << endl; } }; int _tmain(int argc, _TCHAR* argv[]){ A* pa = new A(); pa->f(); B* pb = (B*)pa; pb->f(); delete pa,pb; pa = new B(); pa->f(); //多态 pb = (B*)pa; pb->f(); }
下面语句发生什么变化。
B* pb = (B*)pa;
解析:其实什么变化也没有发生,还是输出A,不存在覆盖问题,pb指向pa原来地址。
虚函数中返回值类型不同,能覆盖吗?
答:不能覆盖,派生类重写基类的虚函数,返回值类型也必须相同。基类默认构造派生类是否需要显示调用
如下代码报错吗?[code]class A{ public: A(int a){} }; class B: public A{ public: B(){} };
解释,在基类没有默认构造的情况下,派生类是需要显式调用。所以上面代码编译不通过。
基类和派生类之间的转换问题
下面程序结果是什么:[code]class A{public: int m_a;}; class B{public: int m_b;}; class C:public A,public B{public: int m_c;}; int _tmain(int argc, _TCHAR* argv[]){ C* pc = new C; pc->m_a = 1; pc->m_b = 2; pc->m_c = 3; B* pb = dynamic_cast<B*>(pc); A* pa = dynamic_cast<A*>(pc); B pbb = *pc; //这里会发生什么? cout << pc << endl; cout << pb << endl; cout << pa << endl; cout << &pbb << "sizeof:" << sizeof(pbb) << "m_b" << pbb.m_b << endl; if (pc == pb) cout << "equal" << endl; else cout << "not equal" << endl; if ((int)pc == (int)pa) cout << "equal" << endl; else cout << "not equal" << endl; }
解析:每个语句解释如下,
1.
A* pa = dynamic_cast<A*>(pc);此时pa指向了子类A的那部分,地址值与pc相同。
2.
B pbb = *pc;这里会发生切割,调用了B类拷贝构造,将B的那部分切割到pbb的所在的栈空间中。
3.
if (pc == pb)这里会发生隐式类型转换,pc = (C*)pb
4.
if ((int)pc == (int)pa)虽然没有隐式类型转换,但地址相同。
dynamic_cast问题
下面代码中哪条语句会出现问题。[code]class A{ public: virtual void foo(){cout << "A foo()" << endl; } void pp(){ cout << "A pp()" << endl; } }; class B:public A{ public: void foo(){cout << "B foo()" << endl; } void pp(){ cout << "B pp()" << endl; } void funB(){ cout << "B::funB()" << endl;} }; int _tmain(int argc, _TCHAR* argv[]) { A a; A *pa = &a; (dynamic_cast<B*>(pa))->foo(); //语句1 (dynamic_cast<B*>(pa))->pp(); //语句2 (dynamic_cast<B*>(pa))->funB(); //语句3 system("pause"); return 0; }
解析:语句1会出现问题。foo()是虚函数,编译器会根据对象的虚函数指针查找虚函数表,定位foo函数。
dynamic_cast不是强制类型转换,而是带有某种“咨询”性质的,如果不能转换,dynamic_cast会返回NULL,表示不成功。
上面3条语句相当于:
[code] B* bnull = NULL; bnull->foo(); bnull->pp(); bnull->funB();
上面的转换时不成功的,所以返回的是NULL指针,又因为pp和funB函数未使用任何成员数据,也不是虚函数,不需要this指针,也不需要动态绑定,所以可以正常运行。
虚拟继承和带有虚函数的继承内存分布情况
下面代码输出结果是多少?[code]class A{ public: virtual void foo(){}; private: char ca[3]; }; class B:virtual public A{ public: virtual void foo(){}; private: char cb[3]; }; class C:virtual public B{ public: virtual void foo(){}; private: char cb[3]; }; int _tmain(int argc, _TCHAR* argv[]) { cout << sizeof(A) << endl; cout << sizeof(B) << endl; cout << sizeof(C) << endl; system("pause"); return 0; }
解析:结果是8,16,24。
然而,下面代码输出结果又是多少?
去掉了虚拟继承
[code]class A{ public: virtual void foo(){}; private: char ca[3]; }; class B:public A{ public: virtual void foo(){}; private: char cb[3]; }; class C:public B{ public: virtual void foo(){}; private: char cb[3]; }; int _tmain(int argc, _TCHAR* argv[]) { cout << sizeof(A) << endl; cout << sizeof(B) << endl; cout << sizeof(C) << endl; system("pause"); return 0; }
解析:结果为8,12,6。
1. 带有虚函数的虚拟继承中,也分两种情况,1.派生类中定义了新的虚函数,并且部分重写了虚基类的虚函数,这个时候,派生类中有3个虚表指针,一个指向虚基类的虚函数指针,一个自己的虚表指针(实现多态),还有一个基类自己虚表指针。2.派生类全部重写虚基类虚函数,且没有新定义虚函数,这时候,派生类中含有两个虚表指针,还有一个基类自己虚表指针,个指向虚基类的虚函数指针。
2. 基类不带虚函数的虚继承中,分两种情况,1.基类和派生类中都没有虚函数,这个时候,派生类只会多添加一个虚表指针,指向虚基类的虚函数(虽然虚基类中没有虚函数)。2.基类中没有虚函数,派生类中有虚函数,则会生产两个虚表指针,一个指向自己虚函数的的虚表指针和一个指向虚基类的虚函数指针。
3. 带虚函数的普通继承中,这个时候不论是基类还是派生类,只要类中有虚函数,都会有且只有一个虚函数指针。
再看下面代码:
验证了第一个的第一种情况。
在看下面:
这验证了第二个的第二种情况。
还有一个很有趣的问题:
怎么没有内存对其了呢?这时如果再定义一个int就会有内存对齐了。如果没有就是以1字节对齐。
最后,总结下,虚拟继承和虚函数多态机制是分开的,虚拟继承会保留基类中的虚表指针,并且添加一个指向虚基类的虚拟指针,它并不会实现多态。
相关文章推荐
- GCC在C语言中内嵌汇编 asm __volatile__
- C语言指针学习(-)
- C语言 大数相加与大数相减
- AndroX eclipse导入源程序后编译错误 make: *** No rule to make target `all'. C/C++ Problem
- C++ int与string的转化
- C语言:将16进制字符串转化为int类型值
- 一个HexToInt的C/C++函数
- C语言相关图书推荐
- C++ Primer 5e chapter 10.2
- C++相关图书推荐
- C语言 - 初级内存
- 实现单例模式C++版本
- C语言 - 结构体
- Java和C++的区别
- 国外程序员整理的 C++ 资源大全
- C++类所占空间总结
- C++: std::string 与 Unicode 如何结合?
- C++——string类和标准模板库
- 深入分析C++中deque的使用
- 用c语言怎样得到一个汉字的GB2312编码