《高质量C++C编程指南》笔记

来源:互联网 发布:淘宝助理 未知错误 1 编辑:程序博客网 时间:2024/04/30 04:39

----2.3----、代码行内的空格

对于表达式比较长的 for语句和if语句,为了紧凑起见可以适当地去

掉一些空格,for (i=0; i<10; i++)if ((a<=b) && (c<=d))

----3.1----、命名规则

全局函数的名字应当使用“动词”或者“动词+名词”(动宾词组)类的成员函数应当只使用“动词”,被省略掉的名词就是对象本身。

例如:

DrawBox(); // 全局函数

box->Draw(); // 类的成员函数

 ----3-2-6----类的数据成员加前缀m_(表示member)

,这样可以避免数据成员与

成员函数的参数同名。

例如:

void Object::SetValue(int width, intheight)

{

m_width = width;

m_height = height;

}

----4.3.1---- 布尔变量与零值比较

不可将布尔变量直接与 TRUEFALSE或者10进行比较。

根据布尔类型的语义,零值为“假”(记为FALSE),任何非零值都是“真”(记为TRUE)TRUE的值究竟是什么并没有统一的标准。

假设布尔变量名字为flag,它与零值比较的标准if语句如下:

if (flag)

// 表示flag为真

if (!flag) // 表示flag为假

----4.3.3 ----浮点变量与零值比较

epsilon  [ˈɛpsəˌlɑn,-lən]希腊语的第五个字母

if ((x>=-EPSILON) &&(x<=EPSILON))

----5.2---- const 来定义常量比用#define来定义常量有更多的优点:

(1) const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安

全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会

产生意料不到的错误(边际效应)

(2)有些集成化的调试工具可以对const常量进行调试,

但是不能对宏常量进行调试。

----5.4----不能在类声明中初始化const数据成员。以下用法是错误的,因为类的对象未被创

建时,编译器不知道SIZE的值是什么。

class A

{...

const int SIZE = 100; // 错误,企图在类声明中初始化const数据成员

int array[SIZE];

// 错误,未知的SIZE

};

const 数据成员的初始化只能在类构造函数的初始化表中进行,例如

class A

{...

A(int size);

// 构造函数

const int SIZE ;

};

A::A(int size) : SIZE(size)

// 构造函数的初始化表

{

...

}

 

A a(100); // 对象aSIZE值为100

 

----6.3---- 创建一个临时对象并返回它

return String(s1 + s2);

这是临时对象的语法,表示“创建一个临时对象并返回它”

。不要以为它与“先创建

一个局部对象 temp并返回它的结果”是等价的,

String temp(s1 + s2);

return temp;

实质不然,上述代码将发生三件事。首先,temp对象被创建,同时完成初始化;

后拷贝构造函数把 temp拷贝到保存返回值的外部存储单元中;最后,temp在函数结束

时被销毁(调用析构函数)

。然而“创建一个临时对象并返回它”的过程是不同的,编译

器直接把临时对象创建并初始化在外部存储单元中,省去了拷贝和析构的化费,提高了

效率。

----7.8----malloc freeC++/C语言的标准库函数,new/deleteC++的运算符。它们都可

用于申请动态内存和释放内存。

对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象

在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于

malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数

和析构函数的任务强加于malloc/free

因此 C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个

能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

由于内部数据类型的

对象”

没有构造与析构的过程,

对它们而言 malloc/freenew/delete

是等价的。

----7.8----new/delete 使用要点

class Obj{

public:

    Obj(void);

    Obj(int x);

 

}

new创建对象数组,只能使用对象的无参数构造函数

Obj *a = new Obj;

Obj *b = new Obj(2);

Obj *arr = new Obj[100];

Obj *arr = new Obj[100](2); //错误

delete对象数组,别忘了[]

delete []arr;

delete arr; //相当于deletearr[0]

 

----8.1.2----函数被C编译器编译后在库中的名字跟 被C++编译器编译后在库中的名字是不同的C++程序要调用已经被编译后的C函数,要用extern“C”

假设某个 C函数的声明如下:

void foo(int x, int y);

该函数被 C编译器编译后在库中的名字为_foo,C++编译器则会产生像_foo_int_int

之类的名字用来支持函数重载和类型安全连接。由于编译后的名字不同,C++程序不能

直接调用 C函数。C++提供了一个C连接交换指定符号extern“C”来解决这个问题

例如:

extern “C”

{

void foo(int x, int y);

... // 其它函数

}

或者写成

extern “C”

{

#include “myheader.h”

... // 其它C头文件

}

这就告诉 C++编译译器,函数foo是个C连接,应该到库中找名字_foo而不是找

_foo_int_intC++编译器开发商已经对C标准库的头文件作了extern“C”处理,所以我

们可以用#include直接引用这些头文件。

 

----8.2.1----重载函数实参隐型类型转换

/**********************************************************************

* Compiler: GCC

* Last Update:  Mon 23 Apr 201203:43:56 PM CST

************************************************************************/

#include <stdio.h>

void f(int a)

{

 

}

 

void f(float a)

{

 

}

 

void g(int a)

{

    printf("g inta\n");

}

 

void g(unsigned char a)

{

    printf("g inta\n");

}

 

int main(int argc, char **argv)

{

    //f(1.1); //error:call of overloaded ‘f(double)’ is ambiguous

    g('a');

    return 0;

}

C++实参数型转换,通过类型提升优于 通过 标准转换 (详见C++Primer4P234

对于C++,整数字面值默认为int,浮点字面值默认为double.

1.1既可以通过标准转换转换成float,也可以转换成int型,产生二义性。

'a'通过类型提升可转换为int型,转换为unsignedchar属于类型转换,故'a'优先转换为int

运行结果:

g int a

 

----8.2----成员函数的重载、覆盖与隐藏

/**********************************************************************

 * Compiler: GCC

 * Last Update:  Mon 23 Apr2012 03:43:56 PM CST

 ************************************************************************/

#include <iostream>

using namespace std;

class Base

{

public:

    virtual void f(floatx)

    {

        cout<< "Base::f(float) " << x << endl;

    }

    void g(float x)

    {

        cout<< "Base::g(float) " << x << endl;

    }

    void h(float x)

    {

        cout<< "Base::h(float) " << x << endl;

    }

};

class Derived : public Base

{

public:

    virtual void f(floatx)

    {

        cout<< "Derived::f(float) " << x << endl;

    }

    void g(int x)

    {

        cout<< "Derived::g(int) " << x << endl;

    }

    void h(float x)

    {

        cout<< "Derived::h(float) " << x << endl;

    }

};

int main(int argc, char* argv[])

{

    Derived  d;

    Base *pb = &d;

    Derived *pd = &d;

    pb->f(3.14f); //Derived::f(float) 3.14

    pd->f(3.14f); //Derived::f(float) 3.14

   cout<<"****************************************"<<endl;

    // Bad : behaviordepends on type of the pointer

    pb->g(3.14f); //Base::g(float) 3.14

    pd->g(3.14f); //Derived::g(int) 3       (surprise!)

   cout<<"****************************************"<<endl;

    // Bad : behaviordepends on type of the pointer

    pb->h(3.14f); //Base::h(float) 3.14      (surprise!)

    pd->h(3.14f); //Derived::h(float) 3.14

   cout<<"****************************************"<<endl;

}

基类类型的指针可以指向派生类型对象(引用也同个道理),所以使用基类类型指针时,不知道指针所指的对象是什么。无论指向的对象是什么,编译器将它当作基类类型对象。当通过该指针调用非虚函数时,无论实际对象是什么,都执行基类类型定义的函数,如果是调用函数,则在运行时根据实际指向的对象去动态调用相应的函数。(详见C++Primer4P487

pd是派生类指针,pd的所有函数调用都只是调用自己的函数,和多态性无关。

pb的函数调用如果有virtual则根据多态性调用派生类的,如果没有virtual则是正常的静态函数调用,还是调用基类的

 

----隐藏----示例:

/**********************************************************************

 * Compiler: GCC

 * Last Update:  Mon 23 Apr2012 03:43:56 PM CST

 ************************************************************************/

#include <iostream>

using namespace std;

 

class Base

{

public:

    void f(int x);

};

 

class Derived : public Base

{

public:

    void f(char *str);

};

 

int main(int argc, char* argv[])

{

    Derived *pd = newDerived;

    pd->f(10);// error . Base::f(int)Derived::f(char*)隐藏了

 

    return 0;

}

 

----8-3-1----参数缺省值只能出现在函数的声明中,而不能出现在定义体中。

----8.4----如果运算符重载为全局函数,那么只有一个参数的运算符叫做一元运算符,有两个参数的运算符叫做二元运算符。

如果运算符被重载为类的成员函数,那么一元运算符没有参数,二元运算符只有一个右侧参数,因为对象自己成了左侧参数。

----8.5.1 ----函数内联是如何工作的

对于任何内联函数,编译器在符号表里放入函数的声明(包括名字、参数类型、返回值类型)。如果编译器没有发现内联函数存在错误,那么该函数的代码也被放入符号表里。在调用一个内联函数时,编译器首先检查调用是否正确(进行类型安全检查,或者进行自动类型转换,当然对所有的函数都一样)。如果正确,内联函数的代码就会直接替换函数调用,于是省去了函数调用的开销。这个过程与预处理有显著的不同,因为预处理器不能进行类型安全检查,或者进行自动类型转换。假如内联函数是成员函数,对象的地址(this)会被放在合适的地方,这也是预处理器办不到的。

inline仅仅是对编译器的建议,编译器有权利忽略这个建议。那么编译器是如何决定函数内联与否呢?一般情况下关键性因素包括函数体的大小,是否有局部对象被声明,函数的复杂性等等。

----8.5.2----关键字inline必须与函数定义体放在一起才能使函数成为内联,仅将inline放在函数声明前面不起任何作用



原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 employee 拼音转数字 JAVA拼音转数字 C加密字符串 日本女优无码下载迅雷 定义一个字符串类String,实现判断该字符串是否为回文字符串 字符串类String回文字符串 判断字符串是否为回文字符串 RRC ERAB E-RAB Linux用gred命令单词长度大于等于5 使用重定向命令完成2个文件合并 使用重定向完成文本文件的拷贝 Linux查找时候不现实出错信息 Linux去掉某个文件所有者同组用户的写权限 求方程式ax2 GNAB-011 flash制作课件 有数组$array=array(“a”=>2,”b”=>6,”c”=>5) 有数组$array=array(“a”=>2,”b”=>6,”c”=>5) 有数组$array=array(“a”=>2,”b”=>6,”c”=>5)排序 php对一个数组按某个键值排序 有数组$array=array(“a”=>2,”b”=>6,”c”=>5);用php实现对以上数组进 $array=array(“a”=>2,”b”=>6,”c”=>5);用php实现对以上数组大小排序 $array=array(“a”=>2,”b”=>6,”c”=>5) $array=array(“a”=>2,”b”=>6,”c”=>5)排序 Linux去掉某个文件所有者同组用户的写权限 Linux.sed命令显示某个文件的第五行 Linux输出该文件的奇数行 Linuxsed命令在文件的最后一行插入该文件的的统计信息,包含多少行,多少个单词,多个字符 Linuxcut命令 Linux.cut命令 Linux.cut命令提出a文件中 Linux.cut命令提出a文件中 Linux.ls Linux创建文件 Linux修改文件的时间 Linux创建文件 Linux修改文件的时间 数字图像分析