结合实例详解"pure Virtual function called"
作者:阿波
链接:http://blog.csdn.net/livelylittlefish/article/details/9750593
(4年前的一篇文章,翻出来共享一下。)
本实例即为经典的讲解C++继承、虚函数、运行时多态的实例。今天我们再用它作为讲解"pure virtual functioncalled"的实例。(在某些平台上也可能输出"pure virtual methodcalled")
1. 实现基类Shape的area函数
file:testVirtualFunc.cpp
#include <stdio.h>
#include <stdlib.h> #define PI 3.1415926 class Shape
{
private:
double ValuePerSquareUnit; protected:
Shape(double valuePerSquareUnit):
ValuePerSquareUnit(valuePerSquareUnit)
{
} public:
virtual double area() const = 0; double value() const
{
return ValuePerSquareUnit * area();
} virtual ~Shape()
{
printf("Shape::~Shape() is called");
} double getPerSquareUnit()
{
return ValuePerSquareUnit;
}
}; class Rectangle : public Shape
{
private:
double Width;
double Height; public:
Rectangle(double width, double height, double valuePerSquareUnit):
Shape(valuePerSquareUnit),Width(width),Height(height)
{
} virtual ~Rectangle()
{
} virtual double area() const
{
return Width * Height;
} }; class Circle: public Shape
{
double Radius; public:
Circle(double radius, double valuePerSquareUnit):
Shape(valuePerSquareUnit),Radius(radius)
{
} virtual ~Circle()
{
} virtual double area() const
{
return PI * Radius * Radius;
}
}; int main()
{
Rectangle* pr = new Rectangle(30, 20, 10);
Circle* pc = new Circle(15, 10); //invoke Rectangle::area()
printf("rectangle: area = %.2f, PerSquareUnit = %.2f, value = %.2f\n",
pr->area(), pr->getPerSquareUnit(), pr->value());
//invoke Circle::area()
printf("circle : area = %.2f, PerSquareUnit = %.2f, value = %.2f\n",
pc->area(), pc->getPerSquareUnit(), pc->value()); Shape* shape;
shape = pr;
printf("rectangle: area = %.2f, PerSquareUnit = %.2f, value = %.2f\n",
shape->area(), shape->getPerSquareUnit(), shape->value()); shape = pc;
printf("circle : area = %.2f, PerSquareUnit = %.2f, value = %.2f\n",
shape->area(), shape->getPerSquareUnit(), shape->value()); return 0;
}
编译一下看看。
# g++ -o testVirtualFunc testVirtualFunc.cpp
/tmp/ccmGHV0C.o: In function `Shape::Shape(double)':
testVirtualFunc.cpp:(.text._ZN5ShapeC2Ed[Shape::Shape(double)]+0x13): undefined reference to `vtable for Shape'
/tmp/ccmGHV0C.o: In function `Shape::~Shape()':
testVirtualFunc.cpp:(.text._ZN5ShapeD2Ev[Shape::~Shape()]+0x7): undefined reference to `vtable for Shape'
/tmp/ccmGHV0C.o:(.rodata._ZTI9Rectangle[typeinfo for Rectangle]+0x8): undefined reference to `typeinfo for Shape'
/tmp/ccmGHV0C.o:(.rodata._ZTI6Circle[typeinfo for Circle]+0x8): undefined reference to `typeinfo for Shape'
collect2: ld returned 1 exit status
实现基类Shape的area函数即可消除该编译错误。
即将以下area函数
virtual double area() const;
改为
virtual double area() const
{
}
输出结果如下。
# g++ -o testVirtualFunc testVirtualFunc.cpp
# ./testVirtualFunc
rectangle: area = 600.00, PerSquareUnit = 10.00, value = 6000.00
circle : area = 706.86, PerSquareUnit = 10.00, value = 7068.58
rectangle: area = 600.00, PerSquareUnit = 10.00, value = 6000.00
circle : area = 706.86, PerSquareUnit = 10.00, value = 7068.58
2. 将基类Shape定义为纯虚类
即将以下area函数
virtual double area() const;
改为
virtual double area() const = 0;
也可以实现该功能,其输出结果与上相同。
3. 对该纯虚类的程序修改
3.1 在Shape类的构造函数中直接调用纯虚函数
修改Shape的构造函数如下。
Shape(double valuePerSquareUnit):
ValuePerSquareUnit(valuePerSquareUnit)
{
std::cout << "creating shape, area = " << area() << std::endl;
}
则会在编译时出现如下错误:
# g++ -o testPureVirtualFunc testPureVirtualFunc.cpp
testPureVirtualFunc.cpp: In constructor ‘Shape::Shape(double)’:
testPureVirtualFunc.cpp:18: warning: abstract virtual ‘virtual double Shape::area() const’ called from constructor
/tmp/cc2WbWlw.o: In function `Shape::Shape(double)':
testPureVirtualFunc.cpp:(.text._ZN5ShapeC2Ed[Shape::Shape(double)]+0x2c): undefined reference to `Shape::area() const'
collect2: ld returned 1 exit status
即不能在构造函数中直接调用纯虚函数。
(Meyers, 3rd edition, Item 9: "Never call virtual functionsduring construction or destruction.") --in the third edition of Scott Meyers's "Effective C++"
3.2 在Shape类的构造函数中间接调用纯虚函数
修改Shape的构造函数如下。
Shape(double valuePerSquareUnit):
ValuePerSquareUnit(valuePerSquareUnit)
{
//std::cout << "creating shape, area = " << area() << std::endl;
std::cout << "creating shape, value = " << value() << std::endl;
}
其执行结果如下。
# g++ -o testPureVirtualFunc testPureVirtualFunc.cpp
# ./testPureVirtualFunc
pure virtual method called
terminate called without an active exception
Aborted (core dumped)
进入core dump文件调试,可以发现,是在value函数中调用area时出现该问题的。
(gdb) bt
#0 0x00110402 in __kernel_vsyscall ()
#1 0x00a8e690 in raise () from /lib/libc.so.6
#2 0x00a8ff91 in abort () from /lib/libc.so.6
#3 0x06894ba0 in __gnu_cxx::__verbose_terminate_handler () from /usr/lib/libstdc++.so.6
#4 0x06892685 in ?? () from /usr/lib/libstdc++.so.6
#5 0x068926c2 in std::terminate () from /usr/lib/libstdc++.so.6
#6 0x06892d65 in __cxa_pure_virtual () from /usr/lib/libstdc++.so.6
#7 0x08048b00 in Shape::value (this=0x8261008) at testPureVirtualFunc.cpp:27
#8 0x08048cd0 in Shape (this=0x8261008, valuePerSquareUnit=10) at testPureVirtualFunc.cpp:19
#9 0x08048d44 in Rectangle (this=0x8261008, width=30, height=20, valuePerSquareUnit=10) at testPureVirtualFunc.cpp:49
#10 0x08048920 in main () at testPureVirtualFunc.cpp:87
3.3 为什么会出现"pure virtual functincalled"
要想搞清楚这个问题,要思考这两件事情:
a. 具体是在什么时刻调用了纯虚函数?
b. 是谁打印的"pure virtual functioncalled"?
带着这两个问题,我们要深入调用内部,找到根本原因。调试过程如下。
注意,编译时要加上"-g"选项,为调试准备符号。
# g++ -g -o testPureVirtualFunc testPureVirtualFunc.cpp # gdb ./testPureVirtualFunc
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) l
78 virtual double area() const
79 {
80 return PI * Radius * Radius;
81 }
82 };
83
84
85 int main()
86 {
87 Rectangle* pr = new Rectangle(30, 20, 10);
(gdb) b 87
Breakpoint 1 at 0x80488e8: file testPureVirtualFunc.cpp, line 87.
(gdb) r
Starting program: /home/abo/linux/2009-12-28 testPureVirtualFuncCalled/testPureVirtualFunc warning: Missing the separate debug info file: /usr/lib/debug/.build-id/ac/2eeb206486bb7315d6ac4cd64de0cb50838ff6.debug warning: Missing the separate debug info file: /usr/lib/debug/.build-id/88/27308433e33aeefb560f42fb133577c8936f20.debug warning: Missing the separate debug info file: /usr/lib/debug/.build-id/92/8ab51a53627c59877a85dd9afecc1619ca866c.debug warning: Missing the separate debug info file: /usr/lib/debug/.build-id/db/8cb95645d5df469d4aece301cdb5e60087be21.debug warning: Missing the separate debug info file: /usr/lib/debug/.build-id/ba/4ea1118691c826426e9410cafb798f25cefad5.debug Breakpoint 1, main () at testPureVirtualFunc.cpp:87
87 Rectangle* pr = new Rectangle(30, 20, 10);
(gdb) step
Rectangle (this=0x909d008, width=30, height=20, valuePerSquareUnit=10) at testPureVirtualFunc.cpp:49
49 Shape(valuePerSquareUnit),Width(width),Height(height)
(gdb) p this
$1 = (Rectangle * const) 0x909d008
(gdb) p *this
$2 = {<Shape> = {_vptr.Shape = 0x0, ValuePerSquareUnit = 0}, Width = 0, Height = 0}
(gdb) info line Rectangle
Line 42 of "testPureVirtualFunc.cpp" is at address 0x8048d08 <Rectangle> but contains no code.
(gdb) step
Shape (this=0x909d008, valuePerSquareUnit=10) at testPureVirtualFunc.cpp:16
16 ValuePerSquareUnit(valuePerSquareUnit)
(gdb) p this
$3 = (Shape * const) 0x909d008
(gdb) p *this
$4 = {_vptr.Shape = 0x0, ValuePerSquareUnit = 0}
(gdb) x/4 0x909d008
0x909d008: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb) step
19 std::cout << "creating shape, value = " << value() << std::endl;
(gdb) x/4 0x909d008
0x909d008: 0x08048fa8 0x00000000 0x40240000 0x00000000
(gdb) p sizeof(ValuePerSquareUnit)
$5 = 8
(gdb) p sizeof(*this)
$6 = 12
(gdb) p *this
$7 = {_vptr.Shape = 0x8048fa8, ValuePerSquareUnit = 10}
(gdb) x/15i 0x8048fa8
0x8048fa8 <_ZTV5Shape+8>: xor %al,0x8c6e0804(%edi)
0x8048fae <_ZTV5Shape+14>: add $0x8,%al
0x8048fb0 <_ZTV5Shape+16>: cmp $0x8c,%al
0x8048fb2 <_ZTV5Shape+18>: add $0x8,%al
0x8048fb4: add %al,(%eax)
0x8048fb6: add %al,(%eax)
0x8048fb8 <_ZTV6Circle>: add %al,(%eax)
0x8048fba <_ZTV6Circle+2>: add %al,(%eax)
0x8048fbc <_ZTV6Circle+4>: int3
0x8048fbd <_ZTV6Circle+5>: popl (%eax,%ecx,1)
0x8048fc0 <_ZTV6Circle+8>: mov %es:(%eax,%ecx,1),%eax
0x8048fc4 <_ZTV6Circle+12>: or 0x48bd808(%esp,%eax,1),%cl
0x8048fcb <_ZTV6Circle+19>: or %ch,(%eax)
0x8048fcd <_ZTI6Circle+1>: movsl %ds:(%esi),%es:(%edi)
0x8048fce <_ZTI6Circle+2>: add $0x8,%al
(gdb) step
Shape::value (this=0x909d008) at testPureVirtualFunc.cpp:27
27 return ValuePerSquareUnit * area();
(gdb) x/4 0x909d008
0x909d008: test $0x8f,%al
0x909d00a: add $0x8,%al
0x909d00c: add %al,(%eax)
0x909d00e: add %al,(%eax)
(gdb) x/4w 0x909d008
0x909d008: 0x08048fa8 0x00000000 0x40240000 0x00000000
(gdb) step
pure virtual method called
terminate called without an active exception Program received signal SIGABRT, Aborted.
0x00110402 in __kernel_vsyscall ()
(gdb)
我们可以通过C++filt命令对那些看不太懂的符号进行解析,
#c++filt _ZTV5Shape
vtablefor Shape
#c++filt _ZTV6Circle
vtablefor Circle
具体的解释,我们可以查看 c++filt 的 man 手册页。
通过strace命令,我们可以清楚地看到程序在core dump之前调用write在标准输出中打印出"pure virtual method called",如下。
# ./testPureVirtualFunc
pure virtual method called
terminate called without an active exception
Aborted (core dumped)
# strace ./testPureVirtualFunc
其部分strace结果如下。
mmap2(0xa65000, 1410608, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xa65000
mmap2(0xbb8000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x153) = 0xbb8000
mmap2(0xbbb000, 9776, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xbbb000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f28000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7f28af0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xbb8000, 8192, PROT_READ) = 0
mprotect(0xbe7000, 4096, PROT_READ) = 0
mprotect(0x68be000, 16384, PROT_READ) = 0
mprotect(0xa61000, 4096, PROT_READ) = 0
munmap(0xb7f29000, 71683) = 0
brk(0) = 0x8410000
brk(0x8431000) = 0x8431000
write(2, "pure virtual method called\n", 27pure virtual method called
) = 27
write(2, "terminate called without an acti"..., 45terminate called without an active exception
) = 45
rt_sigprocmask(SIG_UNBLOCK, [ABRT], NULL, 8) = 0
gettid() = 25398
tgkill(25398, 25398, SIGABRT) = 0
--- SIGABRT (Aborted) @ 0 (0) ---
+++ killed by SIGABRT (core dumped) +++
Reference
http://www.artima.com/cppsource/pure_virtual.html
http://www.codeproject.com/KB/cpp/PureVirtualFunctionCall.aspx
http://blog.csdn.net/kikikind/archive/2008/07/13/2645316.aspx
http://www.cnblogs.com/whjiang/archive/2007/10/22/932880.html
http://support.microsoft.com/kb/120919/zh-cn
http://support.microsoft.com/kb/120919/en-us/
结合实例详解"pure Virtual function called"的更多相关文章
- JavaScript学习笔记-实例详解-类(二)
实例详解-类(二) //===给Object.prototype添加只读\不可枚举\不可配置的属性objectId(function(){ Object.defineProperty(Object ...
- JavaScript学习笔记-实例详解-类(一)
实例详解-类(一): //每个javascript函数(除了bind())都自动拥有一个prototype对象// 在未添加属性或重写prototype对象之前,它只包含唯一一个不可枚举属性const ...
- 转:【工欲善其事必先利其器】—Entity Framework实例详解
开始本篇文章之前,先说一下Entity Framework 6 Alpha1在NuGet中已可用,原文链接http://blogs.msdn.com/b/adonet/archive/2012/10/ ...
- 转载 《AngularJS》5个实例详解Directive(指令)机制
<AngularJS>5个实例详解Directive(指令)机制 大漠穷秋 本文整理并扩展了<AngularJS>这本书第六章里面的内容,此书近期即将由电子工业出版社出版,敬请 ...
- 《HTML5网页开发实例详解》连载(四)HTML5中的FileSystem接口
HTML 5除了提供用于获取文件信息的File对象外,还添加了FileSystem相关的应用接口.FileSystem对于不同的处理功能做了细致的分类,如用于文件读取和处理的FileReader和Fi ...
- getElementByID、getElementsByName、getElementsByTagName实例详解
getElementByID.getElementsByName.getElementsByTagName实例详解 本文通过实例,详细介绍了getElementByID.getElementsByNa ...
- PHP多进程编之pcntl_fork的实例详解
PHP多进程编之pcntl_fork的实例详解 其实PHP是支持并发的,只是平时很少使用而已.平时使用最多的应该是使用PHP-FMP调度php进程了吧. 但是,PHP的使用并不局限于做Web,我们完全 ...
- mysql数据库分区功能及实例详解
分区听起来怎么感觉是硬盘呀,对没错除了硬盘可以分区数据库现在也支持分区了,分区可以解决大数据量的处理问题,下面一起来看一个mysql数据库分区功能及实例详解 一,什么是数据库分区 前段时间写过一篇 ...
- Vue 实例详解与生命周期
Vue 实例详解与生命周期 Vue 的实例是 Vue 框架的入口,其实也就是前端的 ViewModel,它包含了页面中的业务逻辑处理.数据模型等,当然它也有自己的一系列的生命周期的事件钩子,辅助我们进 ...
随机推荐
- PHP经典算法百钱买小鸡
遇到一道有趣的题,并计算2种方法的效率,发现如果穷举所有组合竟高达1000000次排列~所以简化到了600次.所以,你的一个条件,或者一个运算,可能会提高几千倍的效率! <?php header ...
- 【知了堂学习笔记】java 正则表达式
本文参考网络上面别人的博客知识产出 正则表达式基础 1.句号 假设你想要找出三个字母的单词,而且这些单词必须以“t”字母开头,以“n”字母结束.另外,假设有一本英文字典,你可以用正则表达式搜索它的全部 ...
- GOF设计模式快速学习
这段时间,学习状态比较一般,空闲时基本都在打游戏,和研究如何打好游戏,终于通过戏命师烬制霸LOL,玩笑了.为了和"学习"之间的友谊小船不翻,决定对以往学习过的GOF设计模式做一个简 ...
- Spring之Spring环境搭建
Spring之Spring环境搭建 一.什么是Spring? Spring框架是由于软件开发的复杂性而创建的.Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情.然而,Spr ...
- hihocoder 1866 XOR
题面在这里 拆位分析一下就OK啦 /* y + (y xor x) */ #include<bits/stdc++.h> #define ll long long using namesp ...
- 监控 Linux 系统的 7 个命令行工具
监控 Linux 系统的 7 个命令行工具: " 深入 关于Linux最棒的一件事之一是你能深入操作系统,来探索它是如何工作的,并寻找机会来微调性能或诊断问题.这里有一些基本的命令行工具,让 ...
- HDU 4685 Prince and Princess 二分图匹配+tarjan
Prince and Princess 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=4685 Description There are n pri ...
- 伟大的通信使者——JSON(JavaScript版本)
JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式.它基于 ECMAScript 规范的一个子集,采用完全独立于编程语言的文本格式来存储和表 ...
- maven中跳过单元测试(转)
你可能想要配置 Maven 使其完全跳过单元测试. 可能你有一个很大的系统,单元测试需要花好多分钟来完成,而你不想在生成最终输出前等单元测试完成. 你可能正工作在一个遗留系统上面,这个系统有一系列的失 ...
- 3.3V与5V电平双向转换
http://www.amobbs.com/thread-5541344-1-2.html 3.3V转5V:S3输入为0V时,NMOS管导通,S5=S3=0V:S3输入为3.3V时,NMOS管截止,S ...