<<effective c++>>笔记

来源:互联网 发布:java社区 编辑:程序博客网 时间:2024/05/19 03:18

0 术语:
0.1 声明
0.1.1 变量: 非class中extern int x; 在类中int x
0.1.2 函数:  class GraphNode;
          int numDigits(int number);
0.2 定义:提供代码本体
int x; 在类外
int numDigits(int number)
{
......
}
class Widget {
......
};
0.3 初始化
构造函数
class B{
 public:
 B(int x=0,bool b= true);
}
0.4 调用
bool hasAcceptableQuality(Widget w); 声明
Widget aWidget; 定义
if (hasAcceptableQuality(aWidget)) 调用 

Widget w1;
Widget w2(w1)   定义=copy构造函数=编译
w1=w2;          赋值构造=执行
Widget w3=w2;   定义非赋值

item1 :
c++ 由4部分组成。c,object-oriented c++ ; template c++; stl
item2 :
2.1 const 和#define 区别

因为define在预处理器处理宏,所以不在编译的.o中的符号表中
前提补充,预编译如何处理#define,将宏展开,将符号替换,以致符号表中没有宏
const不在预处理中处理,const在编译中处理。
const double AspectRatio=1.653 这是变量定义。


访问权限:指类or对象与main无关。

2.2 inline 函数代替define 宏
#define 定义的类似函数,如#define CALL_WITH_MAX(a,b) f((a) >(b) ?(a):(b))
则会出错
用inline实现,会做参数检查等。不会出错。

item3:
前后限制,整体意义法则
const char *p 值是固定的
char *const p 指针固定

c++以by value返回对象这一事实,子函数内实际改动的是入参的副本。在子函数外,入参值不变。
使用const则在子函数内不会被改动。

传值是主函数 a=10 ; 子函数set (int a ){a=0};主不变;

传引用问题可以参见:polycomm; 主函数str=NULL; 子函数 s(str) {str=malloc(...)} 中分配内存的错误。
const_cast:将常量转成非常量.

const char * c = "sample text";
char *cc = const_cast<char *> (c) ;

static_cast:静态转换,类c转换。
double d=3.14159265;
int i = static_cast<int>(d);

dymatic_cast: 向上转换
CBase* pb;
CDerived d;
pb = dynamic_cast<CBase*>(&d);

reinterpret_cast: 任意转换

item4:
1 构造函数初始化列表叫初始化。
构造函数内叫赋值.初始化列表不在函数内.
初始化列表和赋值相比,赋值需要在创建的时候,先调用default构造函数,在赋值的时候再调用赋值构造函数。
初始化列表只调用一次copy构造函数。
2 规定:总是在初始化列表中列出所有成员变量。
3 成员初始化次序:
3.1 构造函数base class 早于 derived class 被初始化
3.2 class的成员变量总是以其声明次序被初始化
顺序是: 声明早于初始化列表,初始化列表早于构造函数,函数体内赋值。
wfa中新添加的成员变量,不应该在类中,初始化,应该在类的构造函数的初始化列表,初始化。
igt 例题2
4 两种单例模式
4.1 饿汉模式(java叫法)
CarrierManager* CarrierManager::getInstance(void)
{
  static CarrierManager obj;

//将对象的引用,强转成指针。作为返回值
  return (CarrierManager*) &obj;
}
这是个对象定义就调用default构造函数,即init了。static保证如果无创建,有则不创建。走下一步,reutrn obj,即return 已有的。
注意obj是对象,直接生成空间。所以不用向指针一样,obj=new CarrierManager();
4.2 懒汉模式(java叫法)
CscdResourceMgr* CscdResourceMgr::sInstance = NULL;
CscdResourceMgr* CscdResourceMgr::getInstance(void)
{
  if (sInstance == NULL)
  {
    sInstance = new CscdResourceMgr();
  }
  return sInstance;
}
注意先判断。
关键点:
1)私有构造函数
2)静态私有成员
3)公开访问点getInstance
http://www.cnblogs.com/kkgreen/archive/2011/09/05/2166868.html

三:
item5:
1 编译器可以暗自为class 创建default 构造函数(无参数),copy构造函数,赋值构造函数,以及析够函数。(共4个函数)
2 自定义对象的相应构造函数,则default不会产生
item6:
为驳回编译器自动(暗自)提供的机能,可将相应的成员函数声明为private并且不予实现。
item 7
因为c++明白支出,当derived class对象经由一个base class指针删除,而该base class带着一个non-virtual析构函数,
其结果未有定义--实际执行时通常发生的是对象的derived成分没有被销毁。
而子类的析构函数也没被调用。
1 给base class 一个virtual 析构函数。此后删除derived class和基类部分
(虚函数表上的所有的函数,这上面有子有基)
vptr----子实现1----子实现2---基1---基2
2 抽象类不能实例化
3 最深层派生的那个class其析构函数最先被调用,然后是其每一个base class的析构函数被调用。
(和子类调用积累的构造函数,正好相反,是先执行基类的构造函数)

base class 应该声明一个virtual析构函数。如果class 带有任何virtual函数,它就应该拥有一个virtual析构函数。
(有virtual就会基指针指向子类)
classes 的设计目如果不是作为base classes使用,或不是为了具备多态性,就不该声明virtual析构函数。

item8:
析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序
如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class 应该提供一个普通(而非在析构函数中)执行操作
(try--catch)

item9
在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层)
P49 eg1。 本来基类是接口,希望调用子类的实现函数。但是在构造函数期间,先执行基类的,子类还没有创建。这样会出现下面两种情况。
1 由于 logTransaction 是transaction内的一个pure virtual函数,当pure virtual函数被调用,大多执行系统会中止程序
2 如果logTransaction 是个正常的virtual函数并在Transaction内带有一份实现代码。则发现子类本意使用自己的logTraction,结果却是积累的。
会出现指向子类的指针,本意call子类重载的函数,实际调用的是基类的函数.

solution:
无法使用virtual函数从base classes向下调用,在构造期间,你可以籍由“令derived classes将必要的构造信息向上传递至base class构造函数”

item10
令赋值操作符返回一个reference to *this

item11
同《高质量c++》 9.4和9.6的例子

item12 (没看例子)
copying 函数应该确保复制“对象内的所有成员变量”及“所有base class成分”
不要尝试以某个copying函数实现另一个copying函数。应该将共同机能放进第三个函数中,并由两个copying函数共同调用.

item13
智能指针防止分配后,没执行到free,退出造成泄露。
share_ptr有引用计数。

item16
如果你在new表达式中使用[],必须在相应的delete表达式中也使用[].如果你在new表达式中不使用[],一定不要在相应的delete表达式中使用[]

item20
1 子函数传参,内置+stl用传值
2 除了1,其他全用传引用

item22
1 封装(set,get)
2 访问权限限制,有的需要private

item25
vector<int>类型变量vi, 则vector<int>(vi).swap(vi)是什么意思;
vector 内存空间只会增长,不会减少.
比如vi有10000 item。其中9999个erase。仅仅一个有效。但是空间仍然是10000.
    {
     std::vector<int> tmp;  
     ivec.swap(tmp);
    }    
    加一对大括号是可以让tmp退出{}的时候自动析构
 vi将一个有效的元素,存入tmp中。这样tmp仅仅有一个空间(无效部分不会copy)。然后i和tmp交互。则i只有一个空间。tmp有10000.然后tmp在出大括号后析构.
 (一句话,实际是3,4句话含义)
 

0 0
原创粉丝点击