C++ Design
文章平均质量分 60
专注客户端技术
这个作者很懒,什么都没留下…
展开
-
引用与指针的比较
引用是C++中的概念,初学者容易把引用和指针混淆一起。以下程序中,n是m的一个引用(reference),m是被引用物:int m;int &n = m;n相当于m的别名(绰号),对n的任何操作就是对m的操作。例如有人名叫王小毛,他的绰号是“三毛”。说“三毛”怎么怎么的,其实就是对王小毛说三道四。所以n既不是m的拷贝,也不是指向m的指针,其实n就是m它自己。引用的一些规则如原创 2013-04-14 22:09:35 · 647 阅读 · 0 评论 -
防止自己对自己赋值
在赋值运算符中要特别注意可能出现别名的情况,其理由基于两点。 其中之一是效率。如果可以在赋值运算符函数体的首部检测到是给自己赋值,就可以立即返回,从而可以节省大量的工作,否则必须去实现整个赋值操作。例如,条款16指出,一个正确的派生类的赋值运算符必须调用它的每个基类的的赋值运算符,所以在派生类中省略赋值运算符函数体的操作将会避免大量对其他函数的调用。 另一个更重要的原因是保证原创 2013-04-14 22:23:10 · 999 阅读 · 0 评论 -
父类不可以产生对象,而子类可以的设计方式
要满足这种需求,我这边有三种方法。第一种:父类有纯虚函数,子类没有纯虚函数。但这种方法如果子类和父类都不需要多态特性,无疑是一种比较浪费的做法,因为这样做子类和父类都会带来多出一个vptable,占用四个字节。 第二种:把父类的构造函数设为保护的,子类是public派生来的。class Base{public:protected: Base() {原创 2013-04-15 12:01:54 · 701 阅读 · 0 评论 -
避免遮掩继承而来的名称
相信大家对变量作用域的"名称遮掩"现象已经很熟悉了,比如下面这段代码: int x; //global variable void someFunc() { double x; //local variable std::cin>>x; //read a new value to local x } 这时当原创 2013-04-15 16:39:30 · 636 阅读 · 0 评论 -
C++ 内存泄露检测
C/C++ 编程语言的最强大功能之一便是其动态分配和释放内存,但是中国有句古话:“最大的长处也可能成为最大的弱点”,那么 C/C++ 应用程序正好印证了这句话。在 C/C++ 应用程序开发过程中,动态分配的内存处理不当是最常见的问题。其中,最难捉摸也最难检测的错误之一就是内存泄漏,即未能正确释放以前分配的内存的错误。偶尔发生的少量内存泄漏可能不会引起我们的注意,但泄漏大量内存的程序或泄漏日益增多的原创 2013-04-15 21:26:02 · 673 阅读 · 0 评论 -
UML关系图
UML关系图的含义:实际上,有两种不同的 has-a 关系。一个对象可以拥有另一个对象,其中被包含的对象是包含对象的一部分——或者不是。下图 中,我表示出 Airport拥 Aircraft。Aircraft 并不是 Airport 的一部分,但仍然可以说 Airport 拥有 Aircraft,这种关系称为聚集。另一种 has-a 关系是包含,被包含对象是包含对象的一部分原创 2013-04-15 13:01:47 · 855 阅读 · 0 评论 -
对象计数
在C++中,除非你要为其它事情分神,否则对某个特定类的所有已创建对象计数并不是一件很难的事。有时,简单就是简单,不过某些简单也往往很微妙。例如,假设你有一个类Widget,并且你想实现一种方法,以便在运行期获知到底有多少个Widget对象存在。一个既简单又正确的方法是,在Widget中创建一个static计数器,每次调用Widget构造函数时就将计数器加1,而每次调用Widget析构函数时就将原创 2013-04-15 22:02:30 · 729 阅读 · 0 评论 -
资源管理技术 RAII
1.什么是RAII技术RAII(Resource Acquisition Is Initiialization)是一种利用对象生命周期来控制程序资源(如内存,文件句柄,网络连接,互斥量)的简单技术。RAII的一般做法是这样的:在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给一个对象。这原创 2013-04-15 22:00:00 · 791 阅读 · 0 评论 -
绝不重新定义继承而来的缺省参数值
重新定义一个继承而来的non-virtual函数永远都是错误的,本条款的讨论限制在“带有缺省参数的virtual函数”。virtual函数是动态绑定的,而缺省参数却是静态绑定。对象的所谓静态类型,是它在程序中被声明时所采用的类型。#include "StdAfx.h"#include using namespace std;class Shape { p原创 2013-04-16 16:30:10 · 687 阅读 · 0 评论 -
C++ 编译器生成默认构造函数的四种情况
第一种是类成员中有成员是类对象,并且该成员的类含有默认构造函数,那么C++编译器会帮你给这个类也生成一个默认构造函数,用来调用其成员对象的构造函数,完成该成员的初始化构造。需要强调的是,如果这个成员的类也没有给出默认构造函数,那么C++编译器也不会帮你生成该类的默认构造函数。 第二种情况是这个类的基类有默认构造函数。那么C++编译器也会帮你生成该派生类的默认构造函数,以调用基类的默认原创 2013-04-17 09:32:33 · 644 阅读 · 0 评论 -
C/C++中移位实现乘除法运算
用移位实现乘除法运算 a=a*4; b=b/4;可以改为: a=a b=b>>2;说明:除2 = 右移1位 乘2 = 左移1位除4 = 右移2位 乘4 = 左移2位除8 = 右移3位 乘8 = 左移3位...原创 2013-04-25 23:16:12 · 2365 阅读 · 1 评论 -
在已分配好的内存中多次构造对象的探索
见代码:#include using namespace std; class A {public:A(){cout"call A construct"endl;}~A(){cout"call A destroy"endl;}private:int a; }; class B {public:B(){cout"call B constr原创 2013-04-17 13:31:45 · 620 阅读 · 0 评论 -
绝不能在构造函数与析构函数中调用virtual 函数
1.在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class (比起当前执行构造函数和析构函数那层). #include using namespace std; class Base {public:Base();virtual ~Base();virtual void print();}; Base原创 2013-04-14 22:18:38 · 712 阅读 · 0 评论 -
用一个数组存放不同类型指针的想法
声明一个数组来存放一系列的指针,而这些指针的数据类型不完全相同,想到一种想法,这种情况我们可以定义一个这样的结构,这个结构包含一个整数存放指针的值,一个字符串表明指针的类型。声明一个存放这种结构的元素的数组,可以解决这种问题。原创 2013-04-30 15:02:59 · 1491 阅读 · 0 评论 -
通过代码分析得到一些对虚函数的 理解
#include using namespace std; class Base {public:virtual ~Base();virtual void fun();}; Base::~Base(){}void Base::fun(){cout"call fun in the base class"endl;}class Derv原创 2013-04-16 09:00:28 · 530 阅读 · 0 评论 -
RTTI 之 typeid 的使用
typeid 用于在执行期间获取指针或引用所指对象的实际类型。如果是为带有一个或多个虚函数的类则返回动态类型信息,对于其他类型,返回静态(即编译时)类型的信息。 typeid 表达式形如:typeid(e)这里e是任意表达式或者类型名 如果表达式的类型是类类型且该类包含一个或多个虚函数,则表达式的动态类型可能不同于它的静态类型。 typeid操作符可以与任何类型的表原创 2013-05-02 12:33:30 · 796 阅读 · 0 评论 -
sizeof的使用细节
sizeof的作用就是返回一个对象或者类型所占的内存字节数。sizeof的结果是编译时候的结果。也就是说编译时候就知道了他的结果。所以表达式不会在执行期执行。举个例子:#include using namespace std; int main(){ int i=0; sizeof(i++); cout return 0;}结果是0.s原创 2013-05-02 16:32:16 · 591 阅读 · 0 评论 -
delete this 的用处
In order to understand "delete this" :First Step------dive into "delete p"delete p 执行了哪两个步骤?delete p 是一个两步的过程:调用析构函数,然后释放内存。delete p产生的代码看上去是这样的(假设是Fred*类型的):// 原始码:delete p;if (p != NULL)原创 2013-05-02 20:03:06 · 620 阅读 · 0 评论 -
字符串流的使用
iostream 标准库支持内存中的输入/输出,只要将流与存储在程序内存中的 string 对象捆绑起来即可。此时,可使用 iostream 输入和输出操作符读写这个 string 对象。标准库定义了三种类型的字符串流: istringstream,由 istream 派生而来,提供读 string 的功能。 ostringstream,由 ostream 派生而来,提原创 2013-05-15 21:58:23 · 966 阅读 · 0 评论 -
深入理解C++中关键字 mutable
mutalbe的中文意思是“可变的,易变的”,跟constant(既C++中的const)是反义词。 在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量(mutable只能由于修饰类的非静态数据成员),将永远处于可变的状态,即使在一个const函数中。 我们知道,如果类的成员函数不会改变对象的状态,那么这个成员函数一般会声明成原创 2013-04-16 18:18:50 · 664 阅读 · 0 评论 -
函数查找规则
函数查找的顺序如下:1)查找函数的名字2)从中找出最佳匹配。3)检查是否具有该函数的访问权限4)实参相依之查找原则:根据实参的类型,自动进入相应的类内部、命名空间进行查找。原创 2013-05-01 20:36:57 · 677 阅读 · 0 评论 -
C++的重载,覆盖,隐藏
成员函数的重载(overload)、覆盖(override)与隐藏很容易混淆,C++程序员必须要搞清楚概念,否则错误将防不胜防。1 重载与覆盖成员函数被重载的特征:(1)相同的范围(在同一个类中);(2)函数名字相同;(3)参数不同;(4)virtual 关键字可有可无。覆盖是指派生类函数覆盖基类函数,特征是:(1)不同的范围(分别位于派生类与基类);(2)原创 2013-05-02 19:41:29 · 569 阅读 · 0 评论 -
设计class 犹如设计type
C++就像在其他OOP(面向对象编程语言)一样,当你定义一个新class,也就 定义了一个新type。身为C++程序员,你的许多时间主要用来扩张你的类型系统(type system)。这意味你并不只是class设计者,还是type设计者。重载(overloading)函数和操作符、控制内存的分配和归还、定义对象的初始化和终结....全都在你手上。因此你应该带着和“语言设计者当初设计语言内置类型时”原创 2013-05-16 15:06:38 · 792 阅读 · 0 评论 -
关于类模板重载输入输出流的思考
在模板中输入输出流的重载,若使用友元在类模板中声明,在类模板外实现;那么链接时就会报错,可以使用五种方式来实现输入输出流的重载.第一种方法:将输入流输出流的重载的实现写在类模板中templateclass Test{public: Test(const T& data):m_data(data){} friend ostream& operator & obj)原创 2013-05-14 22:08:27 · 2253 阅读 · 0 评论 -
我谈 C++ volatile关键字的用法
volatile的本意是“易变的”,volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。当要求使用volatile声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读原创 2013-05-03 16:53:23 · 669 阅读 · 0 评论 -
模板编写的一个示例
#include #include using namespace std;templatevoid Swap(T& lhs, T& rhs){ T tmp=lhs; lhs=rhs; rhs=tmp;}template //函数模板的偏特化,对函数的参数类型为指针类型的模板进行特化void Swap(T* lhs, T原创 2013-05-18 16:45:59 · 633 阅读 · 0 评论 -
A类不能实例化,但B类需要使用A类对象的设计策略
方案一二,考虑的前提是A类不为抽象类。如果A类为抽象类的话,A类就不能实例化。方案一:将A类的构造函数和析构函数声明为private,将B类声明为A类的友元class B;class A{friend B; private:A();~A();};A(){}~A(){}class B{public:A m_a;};原创 2013-05-04 14:23:28 · 589 阅读 · 0 评论 -
友元关系与继承
像其他类一样,基类或派生类可以使其他类或函数成为友元(12.5节)。友元可以访问类的private和protected数据。 注解:友元关系不能继承。基类的友元对派生类的成员没有特殊访问权限。如果基类被授予友元关系,则只有基类具有特殊访问权限,该基类的派生类不能访问授予友元关系的类。 每个类控制对自己的成员的友元关系class Base{ friend cl原创 2013-05-19 12:52:03 · 955 阅读 · 0 评论 -
C++ 类模板的特化
说起C++的模板及模板特化, 相信很多人都很熟悉 ,但是说到模板特化的几种类型,相信了解的人就不是很多。我这里归纳了针对一个模板参数的类模板特化的几种类型, 一是特化为绝对类型; 二是特化为引用,指针类型;三是特化为另外一个类模板。其实后两种情况就是template的"偏特化"或"部分特化"。对于默认模板定义不适用的类型,特化非常有用。这里用一个简单的例子来说明这三种情况:// g原创 2013-05-18 18:32:28 · 637 阅读 · 0 评论 -
typename的高级用法
templatevoid print(const C& coll){ typename C::const_iterator pos; typename C::const_iterator end(coll.end()); for (pos = coll.begin(); pos != end; ++pos) { cout << *pos << ' ';原创 2013-05-19 14:04:33 · 766 阅读 · 0 评论 -
运用成员函数模板接受所有兼容类型
templateclass shared_ptr{public: template // 构造,来自任何兼容的 explicit shared_ptr(Y* p); //内置指针 template shared_ptr(shared_ptr const&原创 2013-05-19 21:41:27 · 780 阅读 · 0 评论 -
不要轻忽编译器的警告
很多程序员日常总是不理睬编译器警告。毕竟,如果问题很严重,就会是个错误,不是吗?这种想法在其它语言中相对来说没什么害处,但在C++中,可以肯定的一点是,编译器的设计者肯定比你更清楚到底发生了什么。例如,大家可能都犯过这个错误:class B {public:virtual void f() const;};class D: public B {public:virtua原创 2013-05-20 18:53:34 · 811 阅读 · 0 评论 -
继承层次中 this,名字查找 导致的成员调用问题
这种问题首先得清楚调用的是哪一个版本的函数!!然后就可以知道该函数所在的作用域 代码一:#include using namespace std;class A{public: void CallPrint()const;private: virtual void print()const;};void A::CallPrint()cons原创 2013-05-02 13:14:19 · 563 阅读 · 0 评论 -
对应的new和delete要采用相同的形式
下面的语句有什么错? string *stringarray = new string[100];...delete stringarray;一切好象都井然有序——一个new对应着一个delete——然而却隐藏着很大的错误:程序的运行情况将是不可预测的。至少,stringarray指向的100个string对象中的99个不会被正确地摧毁,因为他们的析构函数永原创 2013-05-20 22:07:38 · 670 阅读 · 0 评论 -
如果写了operator new就要同时写operator delete
让我们回过头去看看这样一个基本问题:为什么有必要写自己的operator new和operator delete?答案通常是:为了效率。缺省的operator new和operator delete具有非常好的通用性,它的这种灵活性也使得在某些特定的场合下,可以进一步改善它的性能。尤其在那些需要动态分配大量的但很小的对象的应用程序里,情况更是如此。例如有这样一个表示飞机的类:类airpla原创 2013-05-21 10:28:52 · 544 阅读 · 0 评论 -
C++中 assert的用法
在这里我们对C++ assert()函数的一些基本应用做一个详细介绍。assert宏的原型定义在中,起作用是如果它的条件返回错误,则终止程序执行,原型定义:#includevoid assert (int expression); C++ assert()函数的作用是现计算表达式expression,如果其值为假(即为0),那么他先向stderr打印出一条出错信息,然后通过调用ab原创 2013-05-07 19:13:32 · 2258 阅读 · 0 评论 -
钻石继承的构造函数问题
见代码:#include using namespace std;class A{public: A(int value):m_val(value){cout void print()const;protected: int m_val;};void A::print()const{ cout cout}class B:virtual原创 2013-05-07 13:47:19 · 737 阅读 · 0 评论 -
C++中 explicit的用法
C++中的explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,既然有"显式"那么必然就有"隐式",那么什么是显式而什么又是隐式的呢?如果c++类的构造函数有一个参数,那么在编译的时候就会有一个缺省的转换操作:将该构造函数对应数据类型的数据转换为该类对象,如下面所示:class MyClass{public: MyClass( int num );}..原创 2013-05-07 14:05:49 · 962 阅读 · 1 评论 -
实现类的特定的set_new_handler,operator new ,operator delete
首先让我们了解下new-handler的行为:在opeartor new抛出异常以反应一个未获满足的内存需求之前,它会先调用一个客户指定的错误处理函数,一个所谓的new-handler。(这其实并非全部事实,operator new真正做的事情稍微更复杂些,详见条款51。)为了指定这个“用以处理内存不足的函数”,客户必须调用set_new_handler,那是声明的一个标准程序库函数:name原创 2013-05-22 13:15:12 · 686 阅读 · 0 评论 -
编写new和delete时需固守常规
让我们从operator new开始。实现一致性operator new必得返回正确的值,内存不足时必得调用new_handler函数,必须有对付零字节内存需求的准备,还需避免不慎掩盖正常形式的new——虽然这比较逼近class的接口要求而非实现要求,正常形式的new描述于条款52. operator new的返回值十分单纯。如果有能力供应客户申请的内存,就返回一个指针指向那块内存。原创 2013-05-22 15:23:19 · 704 阅读 · 0 评论