面向对象编程系列六:句柄类
摘要:(1)句柄类前面的章节中提到,在继承体系中,从派生类对象到基类对象的转换中最终得到的是一个基类对象,派生类的部分会被切掉,而对应的指针或引用的转换则并不改变派生类对象,而是将指针或引用绑定到派生类对象中,这样一来才可以实现运行时的动态绑定,如下:1 void get_prices(Item_Base obj, const Item_Base* pi, const Item_Base& ri)2 {3 obj.net_price(1);4 5 pi->net_price(1);6 ri->net_price(1);7 }上面的代码中,obj是基类对象,无论传进来的是基类对象还
阅读全文
posted @
2013-06-30 19:12
Sophia-呵呵小猪
阅读(414)
推荐(0)
面向对象编程系列五:继承体系中的拷贝构造函数、赋值构造函数和析构函数
摘要:(1)派生类中的拷贝构造函数如果派生类中定义了自己的拷贝构造函数,那么该函数中应该显示调用基类的拷贝构造函数来初始化基类部分:1 class Derived : public Base2 {3 public:4 Derived(const Derived& d)5 : Base(d)6 { }7 };如果省略了上面代码中的第5行,那么将在该派生类拷贝构造函数中调用基类的默认构造函数初始化基类部分,结果导致派生类成员是从d中拷贝过来的,而基类部分却是默认值,这与拷贝应该拷贝整个对象的初衷是违背的。(2)派生类中的赋值构造函数同拷贝构造函数一样,派生类定义中应...
阅读全文
posted @
2013-06-29 22:06
Sophia-呵呵小猪
阅读(975)
推荐(0)
面向对象编程系列四:继承体系中的构造函数
摘要:基类的构造函数不可以被继承,可以为public, protected, private,如果某个构造函数只希望能被派生类访问,那么可以设置为protected。(1)派生类构造函数先调用基类的构造函数在创建一个派生类对象时,需要调用派生类的构造函数,由于派生类对象都包含了一个或多个基类子对象,因此会在构造函数中首先调用基类的构造函数来创建基类子对象。如:1 class Bulk_Item : public Item_Base2 {3 public:4 Bulk_Item():min_qty(0),discount(0.0) { }5 };当要创建一个Bulk_Item对象时,首先调...
阅读全文
posted @
2013-06-29 20:48
Sophia-呵呵小猪
阅读(307)
推荐(0)
面向对象编程系列三:基类和派生类之间的转换
摘要:在面向对象编程系列二中,我们看到派生类对象中包含了其基类的部分,可以说派生类对象也是基类对象,所以存在从派生类类型引用或指针到基类类型引用或指针的自动转换,即可以将派生类对象的引用转换为基类子对象的引用,指针也类似。但是,由于基类类型对象可能是一个独立的对象也可能是一个派生类对象的子对象,所以不可以将基类类型的引用或指针转换为派生类类型的引用或指针。相对于引用或指针的转换,对象的转换更为复杂。一般而言,可以使用派生类类型的对象对基类类型的对象进行初始化或赋值,但不可以将派生类类型对象直接转换为基类类型对象。下面分别简要讲述从派生类到基类的引用转换和对象转换:(1)引用转换如下面的代码:1 do
阅读全文
posted @
2013-06-29 19:35
Sophia-呵呵小猪
阅读(862)
推荐(0)
面向对象编程系列二:虚函数基本概念
摘要:如果基类希望派生类对它的某些成员函数进行重定义,那么该基类需要将成员函数声明为virtual函数,即虚函数,而不希望被重定义的函数则为普通的非虚函数。调用虚函数时编译器需要动态绑定以确定究竟要调用的是哪一个类的函数,而触发动态绑定的不仅要求调用的函数是虚函数,还要求必须通过基类类型的引用或指针进行函数调用。(1)派生类对象可以当做基类对象使用因为每个派生类对象都包含基类部分,所以可以将基类类型的引用绑定到派生类对象的基类部分,也可以用指向基类的指针指向派生类对象。例如:double print(const Item_Base&);Item_Base item;print(item);I
阅读全文
posted @
2013-06-28 17:52
Sophia-呵呵小猪
阅读(503)
推荐(0)
面向对象编程系列一:继承
摘要:面向对象编程基于三个基本概念:数据抽象、继承和动态绑定。在C++中,数据抽象和动态绑定也被称为类和多态,也就是说C++中的数据抽象通过类实现,而动态绑定主要是为了实现多态。面向对象编程的关键思想是多态性(polymorphism),在C++中,多态性仅用于通过继承而相关联的类型的引用或指针。(1)公有继承、保护继承和私有继承通过继承,派生类可以继承基类定义的成员,同时也可以重新定义那些与派生类型相关的成员函数,将函数特化;派生类也可以定义自己的成员变量和成员函数以实现更多的特性。派生类继承基类的基本形式如下:class derivedClass : access-label baseClass
阅读全文
posted @
2013-06-28 16:47
Sophia-呵呵小猪
阅读(210)
推荐(0)
字符串系列二:C++标准库string类型
摘要:C++语言提供了专门处理长度可变的字符串的标准库string类型,标准库负责管理与存储字符相关的内存并提供字符串相关操作,头文件为"string"。(1)string类型的定义和初始化string标准库支持以下几个构造函数:构造函数功能string s默认构造函数,s初始化为空串string s2(s1)将s2初始化为s1的一个副本string s3("value")用一个字符串字面值副本初始化s3string s4(n, 'c')将s4初始化为字符'c'的n个副本string s5(cp,n)将s5初始化为cp所指向数组
阅读全文
posted @
2013-06-27 15:29
Sophia-呵呵小猪
阅读(312)
推荐(0)
字符串系列一:C风格字符串
摘要:(1)C风格字符串C风格字符串就是以'\0'结束的字符数组,字符串字面值就是C风格字符串,它以'\0'结尾,类型为const char*。如下面的几个例子:1 char ca1[] = { 'C','+','+'};//没有'\0'结束符,不是C风格字符串2 char ca2[] = { 'C','+','+','\0'};//有'\0'结束符,是C风格字符串3 char ca3[] = "C++";
阅读全文
posted @
2013-06-27 14:05
Sophia-呵呵小猪
阅读(518)
推荐(0)
C++基础系列八:static类成员
摘要:以下内容主要来自于C++ Primer中文版第四版12.6节。对于一个已经定义的类,有的时候需要定义这个类的一个全局对象来获取或管理全体类对象的相关信息,比如统计所有类对象的数量。但是全局对象会带来问题:全局对象可被用户随意修改,破坏了封装性。因而常常用类静态成员来代替全局对象。通常,非static数据成员存在于类类型的每个对象中,而static数据成员独立于该类的任意对象而独立存在;每个static数据成员是与类关联的对象,而不与类对象关联。类也可以定义static成员函数,static成员函数没有this形参,它可以直接访问类的static成员,但不能直接访问非static成员。(1)使用
阅读全文
posted @
2013-06-26 22:03
Sophia-呵呵小猪
阅读(265)
推荐(0)
C++基础系列七:友元
摘要:友元允许特定的非成员函数能够访问一个类的私有成员,同时阻止一般的访问。通过友元机制,一个类可以将对其非公有成员的访问权授予指定的函数或类。友元的声明以关键字friend开始,只能出现在类定义的内部,但可以出现在类中的任何地方,但通常将友元声明成组地放在类定义的开始或结尾。友元可以是其他的类,普通的非成员函数或其他类的成员函数,下面分别简要介绍。(1)类作为友元如果将一个类B声明为另一个类A的友元,那么这个友元类B的成员就可以直接访问A的私有成员,声明的格式如下:class A{ friend class B;}(2)类的成员函数作为友元如果不是将整个类设为友元,而是将某个类B的成员函数设...
阅读全文
posted @
2013-06-26 14:08
Sophia-呵呵小猪
阅读(221)
推荐(0)
C++基础系列六:sizeof 操作符
摘要:sizeof 操作符的作用是返回一个对象或类型名的长度,返回值的类型是size_t。size_t是标准库中的类型,定义在cstddef头文件中,该文件是C标准库的头文件stddef.h的C++版本,它是一个与机器相关的unsigned类型,其大小足以保证存储内存中的对象的大小。(1)sizeof 操作符的用法sizeof的结果是编译时常量,它的使用有三种形式:a. sizeof (type name)b. sizeof (expr)c. sizeof exprb,c中返回的结果都不是expr的值,而是expr的结果的类型的长度,因此,对于sizeof *p,即使p指向一个无效地址,结果也是合法
阅读全文
posted @
2013-06-25 21:43
Sophia-呵呵小猪
阅读(286)
推荐(0)
C++基础系列五:自增自减操作符、箭头操作符、new和delete操作符
摘要:这一章的内容主要来自C++ Primer中文版第四版的第5章。(1)自增和自减操作符自增和自减操作符有前置和后置两种形式,前置操作使用的是对象加1后的值,后置操作使用的则是对象加1前的值,推荐使用前置操作符,原因如下:前置操作返回的是加1后的值,所以返回的是对象的本身,是左值;而后置操作符需要将对象加1,但是还需要保存加1前的值,对于int类型和指针,编译器可优化掉这些额外开销,但是对于复杂的迭代器类型,可能就需要花费很大的代价,如果使用前置操作符,就可以忽略掉这些性能差异。(2)箭头操作符是为了简化解引用和点操作符点操作符用来获取类类型对象的成员,如果要通过指向对象的指针获取对象的成员,那么
阅读全文
posted @
2013-06-25 16:50
Sophia-呵呵小猪
阅读(282)
推荐(0)
C++基础系列四:多维数组
摘要:C++中的多维数组其实就是数组的数组,二维数组指的就是数组的元素也是数组。(1)初始化二维数组的初始化方式有如下几种:1 int ia[3][4] = {{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};2 int ia1[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};3 int ia2[3][4] = {{1},{3},{6}};//只初始化了每一行的第一个元素4 int ia3[3][4] = {0, 3, 6, 9}//只初始化了第一行的元素对于第3行所显示的初始化方式,剩下的元素的初始化规则同一维数组的初始化方式,即内置类型的初始化为0,类
阅读全文
posted @
2013-06-25 15:06
Sophia-呵呵小猪
阅读(366)
推荐(0)
C++基础系列三:数组
摘要:C++语言提供了两种类似于vector和迭代器类型的低级复合类型——数组和指针(此话出自C++Primer 中文版第四版P109,总觉这话让人看着不舒服……),设计良好的程序只有在强调速度时才在类实现的内部使用数组和指针。数组元素可以是引用外的任何内置类型或者类类型。(1)数组的初始化在没有显示初始化的情况下,数组元素的初始化类似于普通变量:a. 在函数体外(全局)的定义的内置数组,其元素均初始化为0;b. 在函数体内定义的内置数组,其元素无初始化;c. 如果为类类型,不管在哪里定义,都自动调用该类的默认构造函数进行初始化;如果该类没有默认构造函数,则必须为该数组的元素提供显示初始化。显示初始
阅读全文
posted @
2013-06-25 13:54
Sophia-呵呵小猪
阅读(241)
推荐(0)
C++基础系列二:预处理器
摘要:(1)常见预处理器指令集C++继承了C的非常精细的预处理器,以高度受限的方式使用预处理器。预处理器主要的指令及功能如下表:指令功能#空指令,无任何效果#include包含一个源代码文件,把源文件中的#include扩展为文件正文,即把包含.h的文件找到并扩展到#include所在处#define定义宏#undef取消已定义的宏#if条件编译指令,如果为真,则编译下面的代码#ifdef条件编译指令,如果宏已经定义,则编译下面的代码#ifndef条件编译指令,如果宏没有定义,则编译下面的代码#elif条件编译指令。如果之前的if不为真,当前条件为真,则编译下面代码#endif结束一个#if...#
阅读全文
posted @
2013-06-24 19:43
Sophia-呵呵小猪
阅读(522)
推荐(0)
C++基础系列一:变量和基本类型
摘要:C++基础系列主要罗列C++语言的一些基础知识,供基础不牢、记忆模糊和概念混淆时快速查询所用,该系列的章节按照C++ Primer中文版第四版进行整理。(1)基本内置类型C++中基本的内置类型包含整型和浮点类型两种。整型:整数、字符和布尔值,其中整数类型中short, int, long占用的存储空间分别为半个机器字长、一个机器字长、一个或两个机器字长,32位机中int和long类型通常都是一个机器字长;字符有char和wchar_t两种,char类型通常是单个机器字节(8位),wchar_t是宽字符型,通常占用的最小空间为16位,用于扩展字符集,如汉字或日语,这些字符集中的一些字符不能用单个
阅读全文
posted @
2013-06-24 19:33
Sophia-呵呵小猪
阅读(515)
推荐(0)
二叉树系列五:二叉查找树
摘要:(1)二叉查找树的定义二叉查找树是一棵二叉树,如果树不为空,那么需满足如下的性质:1)每一个元素都有一个关键字,且任何两个不同的元素的关键字不相等,即关键字惟一;2)左子树(如果存在)中的关键字小于根节点的关键字;3)右子树(如果存在)中的关键字大于根节点的关键字;4)左右子树也是二叉查找树。(2)二叉查找树的查找、删除和插入a. 查找 二叉查找树的查找从根结点开始,若当前结点的关键字等于要查找的关键字,那么返回当前结点;如果当前结点的关键字小于要查找的关键字,那么从左子树中开始寻找,否则从右子树中开始寻找。C++代码实现如下: 1 template 2 TreeNode* Tree::Fin
阅读全文
posted @
2013-06-24 13:00
Sophia-呵呵小猪
阅读(271)
推荐(0)
二叉树系列四:二叉树的遍历(非递归)
摘要:在二叉树系列三中讲述了二叉树的前序遍历、中序遍历和后序遍历的递归实现,可以看到采用递归实现的代码非常简单,但是代码简单不代表实际运行过程中也能达到最简。下面将要介绍二叉树几种遍历的非递归实现实现。(1)前序遍历 1 template 2 void Tree::PreOrderNonRec(TreeNode* root) 3 { 4 if(root != NULL) 5 { 6 stack*> s; 7 TreeNode* curr = root; 8 while(curr || !s.empty()) 9 {10 ...
阅读全文
posted @
2013-06-20 16:40
Sophia-呵呵小猪
阅读(221)
推荐(0)
二叉树系列三:二叉树的遍历(递归方法)
摘要:二叉树的遍历分为前序遍历、中序遍历、后序遍历和层次遍历四种,下面分别进行简要的阐述并用C++实现:(1)前序遍历前序遍历首先访问结点,然后访问其左右子树,如果使用递归算法,那么具体的实现如下: 1 template<class T> 2 void Tree<T>::PreOrder(TreeNode<T>* root) 3 { 4 if(root != NULL) 5 { 6 cout<<root->data<<endl; 7 PreOrder(root->leftChild); 8 PreOrder(root->r
阅读全文
posted @
2013-06-19 15:56
Sophia-呵呵小猪
阅读(254)
推荐(0)
二叉树系列二:二叉树的C++表示
摘要:为了表示二叉树,定义两个类:TreeNode和Tree,其中TreeNode class是对结点信息的抽象,它包括结点的数据域和分别指向左子树和右子树的指针域;Tree class是对二叉树的抽象,一个二叉树由一个根结点确定,所以其成员变量为一个根结点指针,此外它也抽象了二叉树的各种操作,包括二叉树的遍历等。它的C++基本实现如下:TreeNode class: 1 /* TreeNode.h */ 2 #ifndef TREENODE_H 3 #define TREENODE_H 4 5 template<class T> class Tree; 6 7 template<
阅读全文
posted @
2013-06-19 14:50
Sophia-呵呵小猪
阅读(330)
推荐(0)
二叉树系列一:基本概念
摘要:二叉树为什么那么重要?因为使用二叉树可以使查找与用数组查找一样快,同时在二叉树中添加、删除又能像链表中一样高效,因而掌握二叉树的相关知识是非常重要的。(1)二叉树的递归定义二叉树或者是一棵空树,或者是一棵由一个根节点和两棵互不相交的分别称作根节点的左子树和右子树所组成的非空树,左子树和右子树分别是一棵二叉树。结点的度:结点所拥有的子树的个数称为该结点的度;结点的层次:规定树的根节点的层数为1,其余结点的层数等于它的双亲结点的层数加1;树的深度:树中所有结点的最大层数称为树的深度;树的度:树中各结点度的最大值称为该树的度;满二叉树:所有分支结点都存在左子树和右子树,所有叶子结点都在同一层上,这样
阅读全文
posted @
2013-06-19 14:18
Sophia-呵呵小猪
阅读(339)
推荐(0)
链表系列三:操作单链表(续)
摘要:(7)检测一个单链表是否有环单链表有环表示链表中的某个结点指向其前的某个结点从而构成一个环形,若要检测一个链表中是否有环,可以有以下三种方法:a. 指针数组:所谓指针数组,是指用一个数组存放指针变量。这个思路是在遍历单链表的过程中,将已经遍历的结点放到一个足够大的数组中,然后将当前结点与指针数组中的结点比较,如果已经存在说明单链表中存在环,如果遍历到最后一个结点时还没有得到重复的结点,说明单链表中没有环。b. 快慢指针:使用两个指针遍历单链表,一个一次走两步的快指针和一个一次走一步的慢指针,快指针每走一次都与慢指针比较,如果二者相等,那么单链表有环,如果快指针走到单链表尾部,即NULL,那么说
阅读全文
posted @
2013-06-18 22:04
Sophia-呵呵小猪
阅读(212)
推荐(0)
链表系列三:操作单链表
摘要:1 template 2 void swap(myList list, ListNode* node1, ListNode* node2) 3 { 4 if(node1 == node2) 5 return; 6 if(node1->next == node2) 7 { 8 ListNode* prev = list.FindPrev(node1); 9 node1->next = node2->next;10 node2->next = node1;11 prev->next = nod...
阅读全文
posted @
2013-06-18 13:56
Sophia-呵呵小猪
阅读(251)
推荐(0)
链表系列二:链表的C++表示
摘要:这里以单链表为例阐述如何用C++实现表的基本表示:1)表的结构单链表的每一个结点都是由一个任意数据类型的元素和一个指向下一个元素的指针构成的,如下:通过开始结点A0可以访问整个表元素,最后的一个结点的指针为NULL。2)引入表头结点在实现中,为了实现对各个结点的处理的一致性,引入了一个头结点,头结点是指向链表开始结点A0的结点,这样一来链表的所有结点都有了前驱结点,开始结点(链表实际结点的第一个结点)的插入和删除也就不会特别处理,对空表或非空表的处理也是一致的。引入头结点后的结构如下:其中头结点的数据域可以不保存任何数据信息或者保存如链表长度这样的数据,头结点的指针指向链表的开始结点。3)链表
阅读全文
posted @
2013-06-17 16:12
Sophia-呵呵小猪
阅读(479)
推荐(0)
链表系列一:比较链表与数组
摘要:数组和链表是两种不同的数据存储方式,数组的存储需要一段连续的内存空间,而链表的存储则不需要连续存放,它是通过指针链接各个结点构成一个表结构。链表又分为单链表、双链表、单项循环链表和双向循环链表,单链表中每一个结点只需要保存当前结点的数据和一个指向下一个元素的指针,而双链表还需要保存一个指向上一个元素和指针,循环链表则还需要尾结点中保存一个指向第一个结点的指针。具体的讲,数组和链表的区别主要体现在以下几个方面:1)逻辑结构数组在内存中是连续顺序存储的,使用前必须指定数组大小以便为其分配内存空间,大小固定不能动态改变。对数组插入需要分配新的连续内存空间然后移动所有的数据元素,删除同样需要移动删除元
阅读全文
posted @
2013-06-17 14:34
Sophia-呵呵小猪
阅读(351)
推荐(0)
C++之sizeof操作符
摘要:(1)sizeof操作符的返回值类型size_tsizeof操作符的作用是返回一个对象或类型名的长度,返回值的类型是size_t。size_t是标准库中的类型,定义在cstddef头文件中,该文件是C标准库的头文件stddef.h的C++版本,它是一个与机器相关的unsigned类型,其大小足以保证存储内存中的对象的大小。(2)sizeof操作符的用法sizeof的结果是编译时常量,它的使用有三种形式:a. sizeof (type name)b. sizeof (expr)c. sizeof expr以上返回的结果都不是expr的值,而是expr的结果的类型的长度,因此,对于sizeof *
阅读全文
posted @
2013-06-08 11:41
Sophia-呵呵小猪
阅读(331)
推荐(0)
C++之函数指针
摘要:(1)函数指针的声明所谓函数指针是指指向函数而非对象的指针,这个指针所指向的函数的类型由返回类型和参数表确定,与函数名无关。例如:bool (*pf)(const string& a, const string& b);pf是一个指向函数的指针,该函数的形参是两个const string&类型的字符串,返回值是一个bool类型的值。需要注意的是上面的声明中不等价于下面的写法:bool *pf(const string& a, const string& b);因为上面是一个普通的函数声明,它的形参也是两个const string&类型的字符串,返
阅读全文
posted @
2013-06-07 17:19
Sophia-呵呵小猪
阅读(264)
推荐(0)
C++之模板中的typename和class
摘要:在C++模板使用中,通常会用到<typename T>和<class T>两种方式来标识T参数的类型,二者是可以互换的,其主要差别是在含有嵌套从属类型名称(指代码中的某些类型存在依赖关系,比如某些类型需要依赖于参数T)时,需要考虑前缀typename::,否则C++解析时未必会将之后的字符串当做一个类型,或者会将其看做一个变量名称等,例如:template<typename C>void print(C& container){ C::const_iterator* x;}上述代码中C::c onst_iterator未必会被编译器当做一个类型看待,
阅读全文
posted @
2013-06-06 10:55
Sophia-呵呵小猪
阅读(263)
推荐(0)
C++模板编程中的隐式接口和编译期多态
摘要:(1)显示接口和运行期多态在一般的类的实现和使用中,对象的类型是明确的,调用的函数也是在源代码中明确声明的,如果是virtual函数,具体要调用的哪个类的函数会在运行时决定,即运行期多态,如下代码: 1 class Widget{ 2 public: 3 Widget(); 4 virtual ~Widget(); 5 virtual size_t size() const; 6 virtual void normalize(); 7 void swap(Widget& other); 8 ... 9 };10 11 12 void doProcessing(Widget& w
阅读全文
posted @
2013-06-05 22:37
Sophia-呵呵小猪
阅读(968)
推荐(0)
C++之多重继承
摘要:C++中,所谓的多继承是指一个派生类可以有多个基类,这样就可能带来以下几方面的问题:(1)多个基类中存在相同名称时如果一个派生类继承的多个基类中包含有相同名称的函数时,有可能会产生调用不明确,即发生歧义,比如如下代码: 1 class BorrowableItem 2 { 3 public: 4 void checkOut(); 5 }; 6 7 class ElectronicGadget 8 { 9 public:10 bool checkOut() const;11 };12 13 class MP3Player : public BorrowableItem, public Elec.
阅读全文
posted @
2013-06-05 21:08
Sophia-呵呵小猪
阅读(290)
推荐(0)
C++中virtual函数中的缺省参数值不应被派生类重新定义
摘要:C++中派生类从基类继承之后,non-virtual函数不应被重新定义,因为它会导致派生类对象的行为不一致性,也违背了public的"is a"继承关系。除了non-virtual函数不该被派生类重新定义外,基类virtual函数中的缺省参数值也不应该被派生类重新定义,其主要原因是virtual函数是动态绑定,而缺省参数值却是静态绑定。(1)对象的动态类型和静态类型对一个对象,其静态类型指其被声明时的类型,其动态类型则是指其所指的具体对象的类型。例如下面的继承体系: 1 class Shape 2 { 3 public: 4 enum ShapeColor { Green,
阅读全文
posted @
2013-06-05 11:32
Sophia-呵呵小猪
阅读(534)
推荐(0)
C++中不应该为从基类继承的non-virtual函数重新定义
摘要:在C++中,派生类不应该对从基类继承的non-virtual函数重新定义,首先看下面一段代码: 1 class B 2 { 3 public: 4 void mf(); 5 ... 6 }; 7 8 class D:public B 9 {10 ...11 };12 13 D x;14 B* pb = &x;15 pb->mf();16 17 D* pd = &x;18 pd->mf();上面的代码中第15行和第18行调用的函数都是mf,且pb和pd都是指向对象D的,那么这两次调用是否是同一个函数呢?答案是不一定,因为如果D中重新定义了mf函数,那么15行调用的是B
阅读全文
posted @
2013-06-04 22:05
Sophia-呵呵小猪
阅读(172)
推荐(0)
C++利用纯虚函数和虚函数实现接口继承和实现继承
摘要:C++的纯虚函数导致声明它的类成为抽象类,即不可以实例化,派生于这个基类的类必须重新声明所有的纯虚函数。通常纯虚函数不在基类中定义,所以派生类实际上是继承了这些纯虚函数形式的接口,它告诉派生类:你必须实现这些纯虚函数的接口,但是具体的实现方式我不干涉,由你决定!例如在绘图基类Shape中有一个纯虚函数为draw,对于正方形、圆形等要求必须有一个对应的draw函数,但具体的draw由具体的派生类决定,基类函数只是告诉你必须声明和定义这个draw函数。当然,纯虚函数在基类中也是可以定义的,但对它的调用需要由类名进行指定,例如:1 Shape* re = new Rectangle;2 re->
阅读全文
posted @
2013-06-04 20:26
Sophia-呵呵小猪
阅读(889)
推荐(0)
C++继承中的名称遮掩
摘要:1)public继承中解决名称遮掩的方法——引入using声明式所谓名称遮掩是指如果变量或函数的名称相同时,局部的会遮掩外围的,派生类会遮掩基类的,如: 1 class Base 2 { 3 private: 4 int x; 5 public: 6 virtual void mf1() = 0; 7 virtual void mf1(int); 8 virtual void mf2(); 9 void mf3();10 void mf3(double);11 ...12 };13 14 class Derived : public Base15 {1...
阅读全文
posted @
2013-06-04 16:59
Sophia-呵呵小猪
阅读(375)
推荐(0)
C++实现之——降低文件间的编译依存关系(未完待续)
摘要:1)文件间存在的编译依存关系在C++中,如果没有将接口和实现分离,那么在修改起一个成分,就有可能导致编译时许多文件的重新编译,如定义类如下: 1 class Person 2 { 3 public: 4 Person(const string& name, const Date& birthday, const Address& addr); 5 string name() const; 6 string birthDate() const; 7 string address() const; 8 private: 9 string theN...
阅读全文
posted @
2013-06-04 14:46
Sophia-呵呵小猪
阅读(483)
推荐(0)
C++实现之——inline
摘要:inline函数的本质是对函数的每一个调用都以函数本体替换它,它的优点主要有:1)没有一般函数的调用成本;2)方便编译器进行优化:编译器最优化机制通常被设计用来浓缩那些“不被函数调用”的代码,所以当你inline某个函数,编译器就有能力对函数本体执行语境相关最优化,大部分编译器不会对着一个“outlined函数调用”动作执行如此之最优化。然而,对于inline,还有下面几个问题是需要注意的:1)由于inline真正的操作时用函数本体代替调用的代码,因此容易造成程序体积过大,这样一来,代码膨胀可能造成额外的换页行为,降低指令高速缓存装置的击中率,引发效率损失;反之,如果函数的本体很小,编译器针对
阅读全文
posted @
2013-06-03 22:26
Sophia-呵呵小猪
阅读(295)
推荐(0)
C++实现之——异常安全性保证
摘要:定义类PrettyMenu如下: 1 class PrettyMenu 2 { 3 public: 4 ... 5 void changeBackground(istream& imgSrc); 6 ... 7 private: 8 Mutex mutex; 9 Image* bgImage;10 int imageChanges;11 };View Code 成员函数changeBackground的实现如下:1 void PrettyMenu::changeBackground(std::istream& ...
阅读全文
posted @
2013-06-03 20:43
Sophia-呵呵小猪
阅读(363)
推荐(0)
C++实现之——尽量避免返回handles指向对象内部成分
摘要:定义下面的类和结构: 1 class Point 2 { 3 public: 4 Point(int x, int y); 5 ... 6 void setX(int newVal); 7 void setY(int newVal); 8 }; 9 struct RectData10 {11 Point ulhc;12 Point lrhc;13 };14 class Rectangle15 {16 ...17 private:18 std::tr1::shared_ptr<RectData> pData; 19 ...
阅读全文
posted @
2013-06-03 16:56
Sophia-呵呵小猪
阅读(494)
推荐(0)
C++实现之——转型
摘要:C++中的转型语法有三种形式:1)C风格的转换:T(expression)(T)expression上面两种形式都是将expression转换为T类型。2)C++新型转换:a. const_cast(expression) 将对象的常量性转除,是唯一有此能力的C++风格转型操作符;b. dynamic_cast(expression) 主要用来执行“安全向下转型”,也就是觉得对象是否归属继承体系中的某个类型。它是唯一无法由旧式语法执行的动作,也是唯一可能耗费重大运行成本的转型动作。c. reinterpret_cast(expression) 意图执行低级转型,实际动作可能取决于编译器,因此.
阅读全文
posted @
2013-06-03 15:40
Sophia-呵呵小猪
阅读(218)
推荐(0)
C++实现之——延后变量定义
摘要:如果你定义了一个变量而其类型带有一个构造函数和析构函数,那么当程序的程序流到达这个变量定义式时,你便得承受构造成本;当这个变量离开其作用域时,你便得承受析构成本,即使这个变量最终并未被使用,你仍需耗费这些成本,如下面的代码: 1 string encryptPwd(const string& pwd) 2 { 3 string encrypted; 4 if(pwd.length() < MinimumLength) 5 { 6 throw logic_error("Pwd is too short!"); 7 } 8 ...
阅读全文
posted @
2013-06-03 12:28
Sophia-呵呵小猪
阅读(271)
推荐(0)