明明白白c++ effective c++ 条目16-20
来源:互联网 发布:dota2 知乎 编辑:程序博客网 时间:2024/06/05 01:53
effective c++的下载地址
http://download.csdn.net/detail/mlkiller/5335383
前言
这几天看来一些学习c++的书籍推荐。
effective c++ 是很多人推荐的,这个也是学习c++的必读之书。
但是个人觉得这本书可能不是特别适合初学c++的人去学习,因为这里面讲的还是比较深,初学者很多精力都集中在编译错误,或者循环控制等等上面,对于语言的细节,还一些精妙的设计反而会让初学者有些害怕,一方面读不懂这本书讲的什么意义,另一方面不明白effective在哪里。
其实,刚开始看这本书的时候,我也被名字直观翻译所混淆,因为这本书会介绍如何提高代码的运行效率,但是读完之后,发现所谓的effective的含义应该是广义的,对于编程呢,我觉得应该有这几点都可以叫做 effective,
第一,代码易读,其实代码是给人看的,这个其实是最重要的一点。
第二,代码的正确性,这本书讲了很多常犯的错误,少犯错误就是提高效率。
调试代码,甚至测试代码花费的时间,往往比写代码的时间要多。
第三,保证你自己写的代码的健壮性,这本身上面讲了很多这方面的问题,有种错误叫不可以预知,就是不是必先的错误确实很难查找。
第四,提高c++自身的效率,比如使用常引用作为函数参数。
条款16: 在operator=中对所有数据成员赋值
看到这个条款,我的第一印象是,赋值构造函数肯定会对所有数据成员赋值的,这个条款看似多余的。
但是看了文章中写的才明白,主要有两个目的:
第一,写赋值构造函数的时候,要注意,包括后来维护的人,如果新加元素,记得在赋值构造函数里面赋值。
第二,这里主要强调的,继承类(子类)的时候,赋值构造函数不用忘掉父类的构造函数,直接上例子。希望大家也编写一下。
#include<iostream>using namespace std;class A{public:A(int a); A& operator=(const A &a); virtual void print();private:int data;};A::A(int a):data(a){}A& A::operator=(const A &a){if(this == &a)return *this;data = a.data;return *this;}void A::print(){cout<<"A"<<data<<endl;}class B:public A{public:B(int i);B& operator=(const B& b); virtual void print();private:int data;};B::B(int i):A(i),data(i){}B& B::operator= (const B& b){if (this == &b)return *this;data = b.data;}void B::print(){A::print();cout<<"B"<<data<<endl;}class C:public A{public:C(int i);C& operator=(const C& c); virtual void print();private:int data;};C::C(int i):A(i),data(i){}C& C::operator= (const C& c){if (this == &c)return *this;static_cast<A&>(*this) = c;data = c.data;}void C::print(){ A::print();cout<<"C"<<data<<endl;}int main(){B b1(1);B b2(2);b1 = b2;cout<<"-----b1 value--------"<<endl;b1.print();cout<<"-----end--------"<<endl;C c1(1);C c2(2);c1 = c2;cout<<"-----c1 value--------"<<endl;c1.print();cout<<"-----end--------"<<endl;}
这个例子有两点需要注意:
1 继承类怎么访问基类的虚函数。
A::print(),这样调用才可以,我开始的想法是先把继承类转换为基类的指针或者引用,再去调用函数。
但是这个有个明显的问题,因为虚函数本身的含义就是用基类的指针或者引用去调用的,这样就等于是死循环。(自己一直调用自己)
2 继承类怎么访问基类的成员变量。
这个就是先转换为基类的指针或者引用,然后访问。其实非虚函数也可以这么访问。
打印出来的结果是:
-----b1 value--------
A1
B2
-----end--------
-----c1 value--------
A2
C2
-----end--------
A1
B2
-----end--------
-----c1 value--------
A2
C2
-----end--------
第一个的基类没有被改变。
条款17: 在operator=中检查给自己赋值的情况
必须检查的,就是成员变量的指针指向同一块内存。本文只讲这种情况。
还有一种是选择检查的类对象相等。(仅仅是内容相等)
这个目的有两个:
第一,为了效率。 (上面两个都可以提高效率)
第二,保证正确性。(只是针对第一个)
大家看下面的例子
#include<iostream>#include<string.h>using namespace std;class MyString{public: MyString(const char *str=NULL); MyString& operator=(const MyString &str); friend ostream& operator<<(ostream &os, const MyString &str);private: char *data;};MyString::MyString(const char *str){if (NULL == str){data = new char[1];data = '\0';}else{ data = new char[strlen(str)+1];strcpy(data, str);}}MyString& MyString::operator=(const MyString &str){//if (this == &str)//{//return *this;//}delete []data;data = new char[strlen(str.data) + 1];strcpy(data,str.data);return *this;}ostream& operator<<(ostream &os, const MyString &str){os<<str.data;return os;}int main(){MyString a = "abc";MyString &b = a;a = b;cout<<a<<endl;}
注释掉的就是判断指向内存相等。
但是发现个奇怪的问题,
我在cygwin里面编译打印出来的结果居然还是abc,而且单步调试,发现delete的时候指向的内存也没有改变。
开始我怀疑代码有问题,于是在vs2010里面编译了一下,发现运行的结果就是乱码了。
所以猜想cygwin里面不是没有问题,只是还没有爆发,所以在a=b前面加了一句话int *str = new int[1000];这个时候重新运行就发现乱码了。
可能是g++编译的时候发现没有其他内存请求,就没有释放。但是指针的大小已经清空了。具体原因可能还需要高人解释一下。
有人说你真正能理解上面的这几个函数的写法,你就掌握了60%的c++.
条款18: 争取使类的接口完整并且最小
我觉得这个条款其实很大,包含的内容应该很大,怎么来设计好一个类。
总结的也很好,就是根据实际情况,使得接口完整,功能齐全,外部使用的都定义为接口。
并且最小,就是简洁的原则,没有用的就摒弃掉。
剩下的可能要大家到具体项目的时候,再慢慢琢磨这句话的意思。
这个有点设计模式的味道了,后面写一篇文章关于设计模式。
条款19: 分清成员函数,非成员函数和友元函数
对于成员函数和非成员函数:如果有虚函数的需求,必须是成员函数。
非成员函数和友元函数的原则,尽可能少用友元函数。
那么什么情况下必须用有元函数呢?就是你要访问类的私有成员的时候。如果仅仅用接口就可以完成,则不必要用有元函数。
下面将书上关于有理数乘法(分数和整数)写一下。
#include <iostream>using namespace std;class rational{public: rational(int numerator = 0,int denominator =1); int num() const; int denom() const; friend ostream &operator<<(ostream &os,rational &rat);private:int numerator;int denominator;};rational::rational(int num, int denom){numerator = num;denominator = denom;}int rational::num() const{return numerator;}int rational::denom() const{return denominator;}ostream& operator<<(ostream &os, rational &rat){os<<"num"<<rat.numerator<<"denom"<<rat.denominator;return os;}rational operator*(rational& input1, rational &input2){return rational(input1.num()*input2.num(), input1.denom()*input2.denom());}int main(){rational input1(2,3);rational input2(3,4);rational output1;rational output2;output1 = input1*input2;output2 = input1*2;cout<<output1<<endl<<output2<<endl;}
但是有个奇怪的地方 output2 = input1 *2;这句话编译不通过,我还没找到原因,编译的错误是无法将int的转换为rational的类型,有点怪异。
有元函数的话,可以看看我之前所有的重载<<的函数,都用的是有元函数。
条款20: 避免public接口出现数据成员
这个条款就不解释了。
功能和数据要分开。
- 明明白白c++ effective c++ 条目16-20
- 明明白白c++ effective c++ 条目6-10
- 明明白白c++ effective c++ 条目11-15
- Effective Object-C 2.0 第一章(条目1和2)
- Effective Object-C 2.0 第一章(条目3)
- Effective Object-C 2.0 第一章(条目5)
- Effective Object-C 2.0 第一章(条目4)
- 明明白白c++ 解读effective c++系列一(条目1-5)
- 《Effective C++》和《More Effective C++》汇总
- 明明白白 c/c++ 函数 参数问题, 函数内部分配空间
- 明明白白学编程(C语言)第一讲!!!
- 明明白白学通C语言(二维码版)
- 明明白白学通C语言(二维码版)
- <Effective C++: 习惯C++> 笔记
- 【C++】《Effective C++》读书笔记汇总
- 《Effective C++》资源管理:条款16-条款17
- 《Effective C++》资源管理:条款20-条款21
- 《Effective C++》读书笔记
- Quartz 2D编程指南(5) - 变换
- OFBIZ启动加载初始化一
- makefile demo
- 快捷键
- php禁用不安全函数
- 明明白白c++ effective c++ 条目16-20
- Quartz 2D编程指南(7) - 阴影
- CTeX里面CTRL-Space和中文输入法的冲突问题解决
- 这些年的这些事
- ubuntu下定时执行工具cron开启关闭重启
- MST_prim
- Linux网络配置
- 访问Mat图像中每个像素的值
- 计算机是怎样工作的