tppabs到底是什么东西?对代码有何影响?如何清除tppabs标签

来源:互联网 发布:安装央视索福瑞数据 编辑:程序博客网 时间:2024/04/30 15:29
因为C++实在是过于复杂了,时不时都会碰见一些晦涩的细节,因此特意发个帖子,留好位置,随时添加


Item 1    前置声明

当不需要class A的完整定义而只需要其名称时,可以通过前置声明来避免包含class A对应的头文件,这样可以减少编译依赖关系


Item 2   using关键字

     1.谨慎执行将整个名子空间引入的操作
     2.不要在头文件中使用using
     3.不要在"#include"指令之前使用using

Item 3 函数指针

    函数指针的传统用途是实现回调机制;

    函数指针指向inline函数是合法的;然而通过函数指针调用inline函数,则不会进行函数展开,因为编译器通常无法在编译期确定函数指针究竟指向哪个函数

    类的非静态成员函数的地址不是一个指针,函数指针不能指向一个非静态成员函数,这种情况需要使用"类成员指针"
 
Item 4 类成员指针

    "类成员的指针"这个术语不是很合适,因为它既不包含地址,行为也不像指针.

    "类成员指针"中包含的并不是地址,而是类成员在对象中的偏移量

    获取非静态成员函数的地址时,得到的不是一个地址,而是一个指向成员函数的指针

    虚拟性是成员函数自身的性质,而不是指向它的指针所具备的性质;因此,一个指向成员函数的指针,通常不被实现为一个简单的函数指针;它的实现应该存储一些信息,例如指向的函数是否虚拟,如何在虚函数表中找到该函数的对应表项等等.

item 5 指针的比较

    C++中,一个对象可以有多个有效地址,因此指针比较
(==)的实质并不是单纯的比较地址,而是在于确认对象同一性的问题

    对于存在派生关系的指针间的比较,编译器在幕后
结合类型信息和原始地址,得出是否指向同一对象的判断

    在处理指向对象的指针或引用时,必须小心避免丢失类型信息:与void *的指针进行比较时,由于类型信息被抹去,只能按照原始地址来进行比较


Item 6  异常公理

    1.    异常是同步发生的,并且只能发生于函数调用的边界
    2.    对象的销毁"应该"是异常安全的:即析构函数、operator delete 和operator delete[]不应该抛出异常
    3.    swap操作不应该抛出异常


Item 7  编写异常安全函数的基本思路

    首先做任何可能抛出异常的事情(但不会改变对象的重要状态),然后使用不抛出异常的操作来改变对象状态作为结束。


Item 8 智能指针

    operator ->的非同寻常之处在于,当它被调用时,并不被消耗掉(consumed)


Item 9 可选的static

    对于类中的operator new、operator delete、operator new[] 和operator delete[],它们都是隐式静态成员函数,static关键字是可选的

Item 10 指针的隐式转换

    “what does the code ‘cout << p’ do when p is a volatile pointer?”

    结果为1,因为不存在针对volatile x *的operator << 重载版本,volatile X *被转换为了bool,值为true

[c++03]4.12 Boolean conversions [conv.bool]

1 An rvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to an rvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false any other value is converted to true.
   
Item 11 常用缩写

    POD        Plain old Data
    POF        Plain old Function
    RVO        Retrun value optimization
    NRV        Named RVO
    ODR        One definition rule
    SFINAE   Substituion error is not a error


Item 12 不完全的封装

    C++的封装机制只实现了访问保护,但没有实现真正的数据隐藏“私有和保护成员只是无法访问,并不是外界看不到。

    从客户端的编译依赖性的角度来看,C++的封装性很可怜——即使只修改不影响接口的内部private成员,所有使用了该类头文件的源代码都需要从新编译——也就是说,private成员实际上是属于类的接口的一部份的!这个事实颇有些让人沮丧,还好,可以通过pImpl惯用法(Bridge模式)来将这种编译依赖性解耦,真正达到内部实现的隐藏。


Item 13 对象切片

    避免切片问题最简单同是也是最好的方法,就是避免基类具体化


Item 14 初始化 VS 赋值

    初始化是将原始存储单元转变为对象的过秤
    赋值是用新的状态替换已定义对象现有状态的过程

    一致性假设:赋值和初始化的效果在值这个角度上看是一致的

Item 15 构造

    对象的各个部份的构造顺序很明确:

    1.虚基类(virtual base)子对象,无论其在层次体系中位于甚么位置
    2.非虚拟的直接基类,按照它们在基类列表中的顺寻(递归应用这三步)
    3.类的数据成员,按照它们声明的顺序
   
    一旦进入构造函数体,所有的操作就是赋值,而不再是初始化
   
    构造函数、拷贝构造函数和赋值运算符不存在被派生类继承的问题

    在类的成员初始化列表中,有两类数据成员不能被初始化:静态数据成员和数组成员;后者必须在构造函数体内部执行赋值
   
Item 16  初始化顺序

    翻译单元间静态变量的初始化顺序是未定义的,而同一个编译内静态变量是顺序初始化的

    Schwarz计数器

Item 17 参数传递和返回值

    标准规定,函数的参数传递和结果返回,在语义上是初始化,而不是赋值;更进一步,语义上还必须是基于copy ctor 的初始化,而不是基于normal cto r的初始化。

    标准允许编译器对于临时对象的创建和拷贝构造函数的调用进行优化,从而直接调用构造函数进行初始化;但前提是要在未优化前语义合法——也就是说,即使实现不同于语义,语义的合法性仍然必须要保证。

    在处理函数返回值的问题上,标准同样允许编译器进行优化,在维持原有语义不变的情况下消除临时变量和拷贝构造的过程。

Item 18  New

    需要分清楚:operator new 和placement new是被视为同名重载函数的,但operater new 和operator new[]则是名字不同的函数;同理,operator delete和operator delete[]也不是同名函数


    运行时系统保证:在使用new运算符创建对象时,如果operator new成功分配内存,而类的构造函数抛出了异常,则运行时系统将调用适当的operator delete来回收存储单元


Item 19 异常

    当有异常抛出时,运行时异常处理机制会将异常对象复制到“安全“位置的临时存储单元中;这个"安全"位置是平台相关的,但都保证在这个异常完成处理之前,将始终安全的存放在此处。
   
    不要抛出指针,应该抛出对象

    尽量重新抛出异常,而不是抛出新异常

Item 20  基类的析构函数

    虚拟静态函数:

    当基类的析构函数为虚时,对基类指针执行delete操作时,将调用最特别的那个member operator delete,这样就获得了静态虚拟函数的效果。
   
    一方面,operator new 和operator delete都是默认的静态成员函数

    另一方面,标准保证,由于基类的析构函数为虚,将会在“动态类型上下文“中调用member operator delete;由于派生类的析构函数处于派生类的scope中,所以会优先调用派生类的member operator delete(如果存在的话)


Item 21 虚基类
   
    对于存在虚基类的多重继承体系,不难想像,只有位于继承树最远端的派生类才拥有足够的信息则正确的安排虚基类子对象的位置;自然的,负责构造虚基类子对象的任务也就落在这个最远端的派生类身上了。
   
    举例来说:
   
    class A {};
    class B:virtual public A{}:
    class C:virtual public A{};
    class D:public B,public C{};
   
    当生成类型D对象时,由D的构造函数负责构造自虚基类A对应的子对象,而B、C的构造函数不对A进行构造
   
    通常情况下,作为虚基类的类应该只是个接口类,不包含数据


Item 22 类型代码

    在面向对象编程中,对象的类型是用其行为标示的,而不是其状态

    在C++中,永远不要在设计的面向对象部份中使用类型代码(基于类型作出分支选择)


Item 23 内存布局
      
    不要相当然的认为:在单继承的情况下,派生类对象中的基类子对象的地址和完整对象的地址是想同的
   
    反例:基类不含虚函数,而派生类中增加了虚函数,这样由于Vptr的缘故,上述的错误认识很可能不成立


Item 24 虚函数

    避免隐藏非虚拟函数

    避免对虚函数进行重载

    类成员函数的默认参数由静态类型决定,而不是动态类型;所以最好避免在虚函数中使用默认参数

    赋值运算符可以是虚拟的,但很难为这样的行为照到理由;更好的选择是使用prototype模式


Item 25 覆盖、重写、隐藏

    C++中,重载是指用想同的函数名(标识符)来代表相同作用域中声明的不同函数——最后一个限制条件非常重要

    重写只能发生在虚函数之上;覆盖和重写之间没有任何联系;基类的非虚函数只能覆盖,不能重写
   
    访问控制(public or private)不影响重写;不过通常情况下,基类函数和派生类的覆盖版本在访问控制上保持一致性。
   
    覆盖的特殊约束在于:返回类型必须一致,除了"协变返回类型“的特例之外。
   
    在多重继承中,典型情况下,派生类新增加的虚函数会被追加到某个基类的虚函数表中

Item 26   this指针

     对于类X的non-const & non-static成员函数而言,this指针的类型为 X * const;而对于const的non-static 成员函数而言,this指针的类型为X const * const

Item 27    类数组

    要培养如下的直觉:对类数组总是持有怀疑的态度;更好的方法是使用类对象指针的数组,最好是直接使用vector

Item 28 代码重用


    不要为了重用已有代码而使用public继承,相反,应该为了被已有代码重用而使用public继承

    代码共享和接口共享都是层次结构设计所追求的目标,然而,接口共享才是更重要的那个

    当将设计的注意力集中到接口继承上后,正确而有效的代码重用通常会自动随之而来

    范围很广或层次很深的继承都可可能说明设计很糟糕


Item 29 自增运算符

    前缀运算符返回l-value,而后缀运算符返回r-value

    前缀和后缀运算符的对象必须是l-value

    对于自增运算符,在其他条件想同的情况下,总是优先使用前缀形式

Item 30  函数外不允许赋值

    下面的程序为何无法通过编译?

#include <stdio.h>

typedef 
struct StructTest
{
    
int a;
    
int b;
}
StructTest;

StructTest func1Num ;
func1Num.a 
= 5;
func1Num.b 
= 6;

int main(int argc, char* argv[])
{
    printf(
"hello") ;

    
return 0 ;
}

    原因:赋值操作只能在函数体内进行,初始化则没有这个限制
    改成如下形式就合法了:

StructTest func1Num = {
    
5,
    
6
}
;

Item 31 Inheritance or Composition?

    “Airplane和Engine之间的关系是继承还是包含?”

    类似这样的设计问题,可以按如下法则得出合适的结论:

    “AirPlane能有两个或更多的Engine么?“

Item 32 对象的生存期

    起始点:构造函数的完成
    终结点:析构函数的开始

Item 33 构造函数与异常

    标准规定:如果对象的基类子对象或者成员子对象的构造函数失败,那么整个对象的构造函数也必须失败

Item 34 pImpl 惯用法和异常安全

    假设类X中包含类A和类B的实例作为成员,且类A和类B的操作并未提供异常安全的保证;即使是这样,通过改变类X的结构,改为用指针间接包含(pImpl 惯用法)而不是直接包含,仍然可以结合"Create temp and swap"惯用法,为类X实现良好的异常安全特性。



原创粉丝点击