1.尽量使用初始化列表而不要再构造函数里赋值,初始化顺序和声明的顺序一致,一些类型如const,引用等,必须使用初始化。对于非内部数据类型成员对象应当采用初始化表,以获取更高的效率。
example:
B::B(const A& a):m_a(a){}只调用了类A的拷贝构造函数
2.基类都使用虚析构函数,这样才能在使用多态时,准确的析构派生类
3.operator>>和operator<<决不能是成员函数。如果f是operator>>或operator<<,让f成为非成员函数。如果f还需要访问c的非公有成员,让f成为c的友元函数。在类的内部,它可以用于静态和非静态成员。
4.尽可能使用const.const关键字实在是神通广大。在类的外面,它可以用于全局或名字空间常量,以及静态对象(某一文件或程序块范围内的局部对象)。在类的内部,它可以用于静态和非静态成员.
对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const,还有,两者都不指定为const:
char *p = "hello"; // 非const指针,
// 非const数据
const char *p = "hello"; // 非const指针,
// const数据
char * const p = "hello"; // const指针,
// 非const数据
const char * const p = "hello"; // const指针,
// const数据
5.尽量用“传引用”而不用“传值”
“通过值来传递一个对象”的具体含义是由这个对象的类的拷贝构造函数定义的。这使得传值成为一种非常昂贵的操作。
为避免这种潜在的昂贵的开销,就不要通过值来传递对象,而要通过引用:
const student& returnstudent(const student& s)
{ return s; }
这会非常高效:没有构造函数或析构函数被调用,因为没有新的对象被创建。

6.返回值:对于赋值函数,应当用“引用传递”的方式返回对象,对于相加函数应当用值传递,因为引用对象在函数结束时被销毁。

例如:

  1. class String
  2. {…
  3. // 赋值函数
  4. String & operate=(const String &other);
  5. // 相加函数,如果没有friend修饰则只许有一个右侧参数
  6. friend String operate+( const String &s1, const String &s2);
  7. private:
  8. char *m_data;
  9. }
  10.  
  11. //String的赋值函数operate = 的实现如下:
  12. String & String::operate=(const String &other)
  13. {
  14. if (this == &other)
  15. return *this;
  16. delete m_data;
  17. m_data = new char[strlen(other.data)+];
  18. strcpy(m_data, other.data);
  19. return *this; // 返回的是 *this的引用,无需拷贝过程
  20. }

对于赋值函数,应当用“引用传递”的方式返回String对象。如果用“值传递”的方式,虽然功能仍然正确,但由于return语句要把 *this拷贝到保存返回值的外部存储单元之中,增加了不必要的开销,降低了赋值函数的效率。例如:

String a,b,c;

a = b; // 如果用“值传递”,将产生一次 *this 拷贝
a = b= c; // 如果用“值传递”,将产生两次 *this 拷贝
对于相加函数,应当用“值传递”的方式返回String对象。如果改用“引用传递”,那么函数返回值是一个指向局部对象temp的“引用”。由于temp是在栈上申请的变量,函数执行完毕后被销毁,将导致返回的“引用”无效。

  1. //String的相加函数operate + 的实现如下:
  2. String operate+(const String &s1, const String &s2)
  3. {
  4. String temp;
  5. delete temp.data; // temp.data是仅含‘\0’的字符串
  6. temp.data = new char[strlen(s1.data) + strlen(s2.data) +];
  7. strcpy(temp.data, s1.data);
  8. strcat(temp.data, s2.data);
  9. return temp;
  10. }

如果函数返回值是一个对象,要考虑return语句的效率。例如
return String(s1 + s2);
这是临时对象的语法,表示“创建一个临时对象并返回它”。不要以为它与“先创建一个局部对象temp并返回它的结果”是等价的,如
String temp(s1 + s2);
return temp;
实质不然,上述代码将发生三件事。首先,temp对象被创建,同时完成初始化;然后拷贝构造函数把temp拷贝到保存返回值的外部存储单元中;最后,temp在函数结束时被销毁(调用析构函数)。然而“创建一个临时对象并返回它”的过程是不同的,编译器直接把临时对象创建并初始化在外部存储单元中,省去了拷贝和析构的化费,提高了效率。

7.划分全局名字空间的好处
例如,假设library1.h定义了一些常量,其中包括:

const double lib_version = 1.204;

类似的,library2.h也定义了:

const int lib_version = 3;

很显然,如果某个程序想同时包含library1.h和library2.h就会有问题。

要这么做:

  1. namespace sdm {
  2. const double book_version = 2.0;
  3. class handle { ... };
  4. handle& gethandle();
  5. }

用户于是可以通过三种方法来访问这一名字空间里的符号:将名字空间中的所有符号全部引入到某一用户空间;将部分符号引入到某一用户空间;或通过修饰符显式地一次性使用某个符号:

  1. void f1()
  2. {
  3. using namespace sdm; // 使得sdm中的所有符号不用加
  4. // 修饰符就可以使用
  5.  
  6. cout << book_version; // 解释为sdm::book_version
  7. ...
  8.  
  9. handle h = gethandle(); // handle解释为sdm::handle,
  10. // gethandle解释为sdm::gethandle
  11. ...
  12.  
  13. }
  14.  
  15. void f2()
  16. {
  17. using sdm::book_version; // 使得仅book_version不用加
  18. // 修饰符就可以使用
  19.  
  20. cout << book_version; // 解释为
  21. // sdm::book_version
  22. ...
  23.  
  24. handle h = gethandle(); // 错误! handle和gethandle
  25. // 都没有引入到本空间
  26. ...
  27.  
  28. }
  29.  
  30. void f3()
  31. {
  32. cout << sdm::book_version; // 使得book_version
  33. // 在本语句有效
  34. ...
  35.  
  36. double d = book_version; // 错误! book_version
  37. // 不在本空间
  38.  
  39. handle h = gethandle(); // 错误! handle和gethandle
  40. // 都没有引入到本空间
  41. ...
  42.  
  43. }

8.将文件间的编译依赖性降至最低
假设某一天你打开自己的C++程序代码,然后对某个类的实现做了小小的改动。提醒你,改动的不是接口,而是类的实现,也就是说,只是细节部分。然后你准备重新生成程序,心想,编译和链接应该只会花几秒种。毕竟,只是改动了一个类嘛!于是你点击了一下"Rebuild",或输入make(或其它类似命令)。然而,等待你的是惊愕,接着是痛苦。因为你发现,整个世界都在被重新编译、重新链接!
在name.h中定义
class name{};
在person.h中:

  1. #include"name.h"
  2. class person{
  3. public:
  4. person(const name& _pname);
  5. virtual ~Person();
  6. private:
  7. name _pname;//实现细节
  8. string namestr() const;
  9. };

在其他文件中:
class man:public person{
...
};
class woman:public person{
...
};

这时候person文件和name.h之间建立了编译依赖关系,如果name改变了它的实现,或者name依赖的类改变了实现,包含person类的文件以及任何使用了person类的文件就必须重新编译。这时person,man,和woman的文件都要重新编译。
为了实现定义与实现细节分开,我们可以这样定义person:

  1. class name;//提前声明
  2. class person{
  3. public:
  4. person(const name& _pname);
  5. virtual ~Person();
  6. private:
  7. string namestr() const;
  8. };

如果这样做可行,person的用户就不需要重新编译,只可惜用起来才知道:

  1. int main()
  2. {
  3. int x;//定义一个int
  4. person p(new name);//定义一个person
  5. }

当看到x的定义时,编译器知道必须为它分配一个int大小的内存。这没问题,每个编译器都知道一个int有多大。然而,当看到p的定义时,编译器虽然知道必须为它分配一个person大小的内存,但怎么知道一个Person对象有多大呢?
对应于上面的代码,或许可以这样做:

  1. int main()
  2. {
  3. int x; // 定义一个int
  4. Person *p; // 定义一个Person指针,编译器知道是4字节
  5. ...
  6. }

下面具体介绍怎么采用这一技术来实现person接口和实现的分离:

  1. class name;//提前声明
  2. class man;
  3. class woman;
  4. class person{
  5. public:
  6. person(const name& _pname);
  7. virtual ~Person();
  8. man *personimpl1;//指向具体实现类
  9. woman *personimpl2;
  10. string namestr() const;
  11. };
  12. //在person.cpp中:
  13. #include"person.h"
  14. #include"man.h"
  15. person.cpp::person(const name& _pname)
  16. {
  17. personimpl1 = new man(_pname);
  18. }
  19. string person::namestr()const
  20. {
  21. return personimpl1->name();
  22. }

这样,person作为接口与实现完全分离。编译时不对name的改变产生依赖

effectiveC++ 内存管理 学习笔记的更多相关文章

  1. C++内存管理学习笔记(5)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

  2. C++内存管理学习笔记(6)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

  3. C++内存管理学习笔记(7)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

  4. C++内存管理学习笔记(4)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

  5. C++内存管理学习笔记(3)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

  6. C++内存管理学习笔记(2)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

  7. C++内存管理学习笔记(1)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

  8. Linux内存管理学习笔记 转

    https://yq.aliyun.com/articles/11192?spm=0.0.0.0.hq1MsD 随着要维护的服务器增多,遇到的各种稀奇古怪的问题也会增多,要想彻底解决这些“小”问题往往 ...

  9. Unity移动游戏加载性能和内存管理-学习笔记

    前言 正在学习Doctor 张.鑫大佬的移动游戏加载性能和内存管理,内容非常非常的干,所以我烧了很多开水,边喝边看,一边拿小本几做好笔记 本文只是关于前2章的内容笔记,关于各种资源的加载耗时 纹理资源 ...

随机推荐

  1. C#版BitStream 1.0

    根据C++版的改编,刚刚改完,估计使用会有问题,对于uint8处理的不好 关于使用: BitStream bs = new BitStream( ); bs.WriteInt32( ); int a ...

  2. mui学习记录

    1.页面间传值 2.mui如何增加自定义icon图标 http://ask.dcloud.net.cn/article/128 3.设计基于HTML5的APP登录功能及安全调用接口的方式(原理篇) h ...

  3. java 多态和内部类

    接口跟接口之间存在继承关系 一个接口可以继承多个接口 一个非抽象类:必须实现接口中的所有方法 一个抽象类实现接口  可以不实现接口中的方法  但是继承与抽象类的类必须要是实现接口中的方法 多态:一个对 ...

  4. windows下常查看端口占用方法总结

    启动Tomcat时又出现端口占用错误.现在把对端口的处理命令和方法进行汇总. 1.查看所有连接的PID 开始--运行--cmd  ,输入netstat -ano 找到端口号对应的PID后,从任务管理器 ...

  5. BFS_Maze_求解迷宫最短路径

    /* 10 10 #.######.# ......#..# .#.##.##.# .#........ ##.##.#### ....#....# .#######.# ....#..... .## ...

  6. c语言学习笔记三

    第三章,函数 字符串函数 //strcmp int my_strcmp(char *str1,char *str2) {   while(*str1 = = *str2)   /*不可用while(* ...

  7. SQUID常用命令

    Squid日常维护过程中,常用的一些命令: 1,初始化你在 squid.conf 里配置的 cache 目录squid -z如果有错误提示,请检查你的 cache目录的权限.可以使用使用更改目录权限: ...

  8. 一、常见PHP网站安全漏洞

    对于PHP的漏洞,目前常见的漏洞有五种.分别是Session文件漏洞.SQL注入漏洞.脚本命令执行漏洞.全局变量漏洞和文件漏洞.这里分别对这些漏洞进行简要的介绍. 1.session文件漏洞 Sess ...

  9. SQL注入以及如何防止和索引

    SQL注入产生的原因:程序开发过程中不注意规范书写sql语句和对特殊字符进行过滤,导致客户端可以通过全局变量POST和GET提交一些sql语句正常执行. 防止SQL注入: 1.开启配置文件中的magi ...

  10. 记录一下折腾webp 的过程

    最近有客户想要处理webp 的动图,情况当然是我们并不能处理webp 格式的图片.这事就交给了我来折腾,一开始想着用瑞士军刀ffmpeg.结果是折腾了差不多一天,前前后后编译了几十次ffmpeg 源码 ...