随笔分类 - C++语法
摘要:下面再来看书,去理解书上说的Handler classes就简单多了,我们大概过一下。假设我们要写一个Person类,如下: 1 class Person 2 { 3 private: 4 string name; 5 MyDate birthday; 6 MyAddress address; 7 8 public: 9 // fallows functions10 // ...11 };这个Person类里面包含有人的名字,人的生日以及地址,还有一些没有列出来的方法。注意到这里用到了string(它不是一个class,只是一个typedef,大多数情...
阅读全文
摘要:在说这一条款之前,先要了解一下C/C++的编译知识,假设有三个类ComplexClass, SimpleClass1和SimpleClass2,采用头文件将类的声明与类的实现分开,这样共对应于6个文件,分别是ComplexClass.h,ComplexClass.cpp,SimpleClass1.h,SimpleClass1.cpp,SimpleClass2.h,SimpleClass2.cpp。ComplexClass复合两个BaseClass,SimpleClass1与SimpleClass2之间是独立的,ComplexClass的.h是这样写的:#ifndef COMPLESS_CLAS
阅读全文
摘要:学过基本程序课的同学都知道,inline是内联的关键字,它可以建议编译器将函数的每一个调用都用函数本体替换。这是一种以空间换时间的做法。把每一次调用都用本体替换,无疑会使代码膨胀,但可以节省函数调用的成本,因为函数调用需要将之前的参数以堆栈的形式保存起来,调用结束后又要从堆栈中恢复那些参数。但注意inline只是对编译器的一个建议,编译器并不表示一定会采纳,比如当一个函数内部包含对自身的递归调用时,inline就会被编译器所忽略。对于虚函数的inline,编译器也会将之忽略掉,因为内联(代码展开)发生在编译期,而虚函数的行为是在运行期决定的,所以编译器忽略掉对虚函数的inline。对于函数指针
阅读全文
摘要:还是举书上的例子:1 void PrettyMenu::changeBackground(std::istream& imgSrc)2 {3 lock(&mutex);4 delete bgImage;5 ++ imageChanges;6 bgImage = new Image(imgSrc);7 unlock(&mutex);8 }这段代码大致的意思就是改变背景图片,删掉旧的背景图片,记录修改次数,然后创建新的背景图片。考虑到多线程操作,所以这里用了lock和unlock。但这里会出现问题,因为并不是每次new都会成功的,有可能抛出异常,一旦抛出异常...
阅读全文
摘要:举个例子: 1 class Student 2 { 3 private: 4 int ID; 5 string name; 6 public: 7 string& GetName() 8 { 9 return name;10 }11 };这是一个学生的类,类里面有两个成员变量,一个是学生ID,用整数表示,另一个是姓名,用string表示。有一个公有的方法GetName(),获得学生的名字,根据条款20所说的,使用引用可以防止资源不必要地拷贝,那么在返回值这边就用string&。但现在问题来了,这个函数只是想返回学生的姓名,并不想用户对之进...
阅读全文
摘要:有关转型的几种做法,已经在早些的博客中写过了。这里先简单回顾一下,再讲一讲effective中对之更深入的阐述。转型可以按风格可以分成C风格转型和C++风格转型两大类,C风格转型很容易看到,因为我们会经常使用,像(T) expression以及:T (expression)最经典的例子就是处理整数除法,在C/C++程序中,整数除法的结果还是整数,有时会得不到我们想到的结果,比如3/5,结果是0,而不是0.6,但如果这样double(3) / 5,结果就会是0.6了,因为转型操作double(3),将整数转成了浮点数,这样就是小数除法了,可以得到带小数点的结果。C++风格的转型操作分成四类:co
阅读全文
摘要:这个条款从字面意思还是很好理解的,就是在使用这个变量前才去定义,而不是很早就定义了它,而在很后面的时候才去使用。这个条款只适用于对变量声明位置没有要求的语言,比如C++。对于像C或者一些脚本语言,语法要求变量声明放在函数开始处,这个条款就不能使用了。但其实从使用的角度而言,如果不是语法的硬性要求,还是在变量使用前再去定义变量的做法比较好。这有几点原因,最直观的就是可读性比较好,程序员在阅读代码时,看到一个陌生的变量名,不用向上翻好几页才看到它的定义类型,而且对于开发者而言,也不会出现前面定义了一个变量,之后又忘记使用它的情况。另一个原因就是可以节省资源,考虑以下的代码:1 void examp
阅读全文
摘要:我也不知道为什么作者给这个条款起这样的名字,因为这样看上去重点是在“不抛出异常”,但事实上作者只是在全文最后一段说了一下不抛异常的原因,大部分段落是在介绍怎样写一个节省资源的swap函数。你可以试一下,只要包含了头文件iostream,就可以使用swap函数,比如:1 #include <iostream>2 3 int main()4 {5 int a = 3;6 int b = 4;7 std::swap(a, b);8 }结果就是a为4,b为3了,也就是说,在std的命名空间内,已经有现成的swap的函数了,这个swap函数很简单,它看起来像这样:1 templa...
阅读全文
摘要:还是举书上的有理数例子: 1 class Rational 2 { 3 private: 4 int numerator; 5 int denominator; 6 public: 7 Rational(int n = 0, int d = 1): numerator(n), denominator(d){assert(denominator != 0);} 8 int GetNumerator() const{return numerator;} 9 int GetDenominator() const {return denominator;}10 ...
阅读全文
摘要:本条款还是讨论封装的问题,举书上的例子:1 class WebBrower2 {3 public:4 void ClearCach();5 void ClearHistory();6 void RemoveCookies();7 };定义了一个WebBrower的类,里面执行对浏览器的清理工作,包括清空缓存,清除历史记录和清除Cookies,现在需要将这三个函数打包成一个函数,这个函数执行所有的清理工作,那是将这个清理函数放在类内呢,还是把他放在类外呢?如果放在类内,那就像这样: 1 class WebBrower 2 { 3 … 4 void Clea...
阅读全文
摘要:这个条款从字面意思上理解很简单,很多程序都是这样做的,即使作为初学者,也知道该将成员变量声明为private,但有没有想过,为什么要这样做?为什么public成员变量不行?protected成员变量行不?举一个简单的例子:1 class Clothes2 {3 public:4 int price;5 string name;6 };假设有一个衣服的类,里面的成员变量用来描述它的价格和衣服名。将之设为public的话,类外可以直接接触到price成员变量。这样很危险,因为客户端可以直接修改price了,卖衣服的商家就会不开心了。而将price设置成private,像这样: 1 ...
阅读全文
摘要:当你理解条款21后,很可能出现一种过度使用的情况:只要看到是一个非内置类型,就去使用引用传值。举个书上的例子:1 class Rational2 {3 private:4 int numerator;5 int denominator;6 public:7 Rational():numerator(0), denominator(1){}8 friend const Rational& operator* (const Rational& r1, const Rational& r2){…}9 };numerator表示分子,denominator表示分母,这里...
阅读全文
摘要:默认情况下,C++的函数调用是传值调用,也就是说形参拷贝实参的内容。举书上的例子: 1 class Person 2 { 3 private: 4 string name; 5 string address; 6 public: 7 Person(){} 8 virtual ~Person(){} 9 };10 11 class Student: public Person12 {13 private:14 string schoolName;15 string schoolAddress;16 public:17 Student(){}...
阅读全文
摘要:这里初看到”class”和”type”,感觉他们是说的是同一样东西,但仔细读了一下,两者在文中还是有区别的。class侧重于自定义的类,而type侧重于系统预定义的类(像int、double、string、vector)。设计好的class,使之像设计type一样,就是说要使自己设计的类像系统预定义的类那样好用,这对设计思想提出了较高的要求。要设计高效的class,需要回答以下的问题:1. 对象如何去创建和销毁?这是构造函数和析构函数的问题,当然如果想要有堆分配方式,还需要重载new、new[]、delete和delete[]运算符;2. 对象初始化与对象的赋值应该有什么样的差别?初始化是构造
阅读全文
摘要:从本条款开始,就进入到全书的第四部分:设计与声明。程序员设计接口时应本的对用户负责的态度,“如果客户企图使用某个接口而却没有获得他所预期的行为,这个代码不应该通过编译;如果代码通过了编译,那它的作为就该是客户所想要的”。举一个书上的例子:1 class Date2 {3 public:4 Date(int month, int day, int year);5 …6 };这个类看上去好像没有问题,提供的构造函数接口简单直观,然而它却做了一个假设——用户都能够按月、日、年的顺序来传参。但事实上,一定会有不少用户记错这个顺序,比如我们常用的顺序是年、月、日,所以有同学会下意识地这样...
阅读全文
摘要:举书上的例子:1 int GetPriority();2 void processWidget(shared_ptr<Widget> pw, int priority);如果这样调用processWidget:processWidget(new Widget(), GetPriority());注意一下两边的类型,左边是管理者shared_ptr<Widget>类,而右边是被管理者Widget类,两边类型不等,编译器会试图执行类型隐式转换,但为了安全起见,shared_ptr的构造函数前面有修饰符explicit,它不允许发生隐式转换,所以编译器会报错。但如果这样:pr
阅读全文
摘要:这个条款比较好理解,就是new有两种形式:1 int *p = new int(); // 申请一个int资源2 int *p = new int[3]; // 申请连续三块int资源而delete也有两种资源:1 delete p; // delete指针所指向的单一资源2 delete [] p; // delete指针所指向的多个连续资源块那么该各用哪一种形式呢?一个简单又好记的规则就是:在new如果使用了[],那么delete也去用[];反之则都不用。一句话,都用或者都不用。但事实上,如果用错了,编译器不会给任何提示,也没有运行错(VS2008环境),这些行为是“未定义的”。这可能会产
阅读全文
摘要:先放上自己写的MySharedPtr类,这是仿照shared_ptr的功能来实现的(实际shared_ptr要复杂的多)。 1 #ifndef MY_SHARED_PTR_H 2 #define MY_SHARED_PTR_H 3 4 #include <iostream> 5 using namespace std; 6 7 8 typedef void (*FP)(); 9 10 template <class T>11 class MySharedPtr12 {13 14 private:15 T *ptr;16 size_t *count;17 F...
阅读全文
摘要:条款十四:在资源管理类中小心copying行为首先来看一个例子: 1 #include <iostream> 2 using namespace std; 3 4 class Lock 5 { 6 public: 7 explicit Lock(int* pm): m_p(pm) 8 { 9 lock(m_p);10 }11 12 ~Lock()13 {14 unlock(m_p);15 }16 17 private:18 int *m_p;19 void lock(int* pm)20 ...
阅读全文
摘要:从这个条款开始,就进入到资源管理部分了。资源管理往往是大型项目的一个难点,也是重中之重,看到一些编程规范,都是将资源管理的规范列为高优先级的重点。管理资源的最好方法其实是预防,而好的预防方法就是尽量不去使用C/C++的原生指针,这些指针像幽灵一样,一个“忘记”,就是一个隐患。当项目小的时候,这些隐患看不出来,但当研发一个拥有上万级用户的产品时,服务器对很多人同时运行含有隐患的代码,这个隐患就会爆发,导致内存不足而崩溃。举个例子,初学者常常这样写:1 int *p = new int();2 …3 delete p;这个当然是OK的,内存可以回收,但万一中间的过程超过数十行,你还能记住去dele
阅读全文