《C++ Primer Plus(中文第6版)》第八、九章(学习自用)

第八章-内联函数、引用变量/传参、默认参数、函数重载、函数模板及具体化

8.1 C++ 内联函数

        内联函数是C++为提高程序运行速度所做的一项改进。常规函数和内联函数之间的主要区别不在于编写方式,而在于C++编译器如何将它们组合到程序中。

        常规函数调用也使程序跳到另一个地址(函数的地址),并在函数结束结束时返回。执行到函数调用指令时,程序将在函数调用后立即存储该指令的地址,并将函数参数赋值到堆栈(为此保留的地址块),跳到标记函数起点的内存单元,执行函数代码(也许还需将返回值放入寄存器),然后跳回到地址被保存的指令处。来回跳跃并记录跳跃位置意味着以前使用函数时,需要一定的开销。

        C++内联函数提供了另一种选择。内联函数的编译代码与其他程序代码“内联”起来了。也就是说,编译器将使用相应的函数代码替换函数调用。对于内联代码,程序无需跳到另一个位置处执行代码,再跳回来。因此,内联函数的运行速度必常规函数稍快,但代价是需要占用更多内存。如果程序在10个不同对方调用同一个内联函数,则该程序将包含该函数代码的10个副本。

        应该有选择地使用内联函数。如果执行函数代码的时间必处理函数调用机制的时间长,则节省的时间将只占整个过程的很小一部分。如果代码执行时间很短,则内联调用就可以节省非内联调用使用的大部分时间。另一方面,由于这个过程相当快,因此尽管节省了该过程的大部分时间,但节省的时间绝对值并不大,除非该函数经常被调用。

        要使用内联函数,必须采取措施:1、在函数声明前加上关键字inline;2、在函数定义前加上关键字inline。(注:程序员请求将函数作为内联函数时,编译器并不一定会满足这种要求。他可能认为该函数过大或注意到函数调用了自己,但内联函数不能递归。而有些编译器没有启用或实现这种特性。)

内联和宏:incline工具是C++新增的特性,C语言使用预处理语句#define来提供宏——内联代码的实现。宏将直接进行文本替换,而不能按值传递。

8.2 引用变量

        引用变量是已定义变量的别名(另一个名称),这样可以使用其他名字来调用该变量。引用变量的主要用于在函数的形参上。通过将引用变量作为参数,函数将使用原始数据而不是副本。这样除了使用指针作为参数,使得函数直接使用原始数据外,还可以使用引用来使用原始参数,即引用传递。引用传递主要的优点在于节省时间和内存。

int rate;
int & rod = rate;

        其中,&不是地址运算符,而是类型标识符的一部分,就像声明中char* 指的是指向char的指针一样,int& 指的是指向int 的引用。上述的引用声明允许将rod指向rate的内存单元,因此它们的值相同。

引用和指针的差别:

  • 必须在声明引用时将其初始化,而不能像指针那样,先声明,再赋值;
  • 引用更接近const 指针(typeName const *p),必须在创建时初始化,且不能修改为其他变量的引用;
int rats = 101;
int *p = &rats;
int &rodents = p;
int bunnies = 50;
p = &bunnies;

        上述代码中,将rodents初始化为*p,使得rodents指向rats。接下来将pt改为指向bunnies,但是这样并不能改变rodents引用的是rats。

        C语言只能按值传递。按值传递导致被调用函数使用调用程序的值的拷贝,当然借助指针在C语言中也能实现引用传递类似的功能。

实际上,对于形参为const引用的C++函数,如果实参不匹配,则其行为类似于按值传递,为确保原始数据不被修改,将使用临时变量存储值。

左值和非左值:

        左值参数是可以被引用的数据对象,例如变量、数组元素、结构成员、引用和解除引用的指针;非左值包括字面常量(如浮点数10.0)(用引号括起来的字符串除外,它们由地址表示)和包含多项的表达式。在C语言中,左值最初指的是可出现在赋值语句左边的实体,但这是引入关键字const之前的情况。现在,常规变量和const变量都可视为左值,因为可以通过地址访问它们。但常规变量属于可修改的左值,而const变量属于不可修改的左值。

注意:如果函数调用的参数不是左值或与想用的const引用参数的类型不匹配,则C++将创建类型正确的匿名变量,将函数调用的参数的值传递给该匿名变量,并让参数来引用该变量。

应尽可能使用const:

  • 使用const 可以避免无意中修改数据的编程错误;
  • 使用const 使函数能够处理const和非const实参,否则将只能接受非const参数;
  • 使用const 引用使函数能够正确生成并使用临时变量。

C++11 新增了另一种引用——右值引用(rvalue reference)。这种引用可指向右值,是使用&&声明的。新增右值引用的主要目的是,让库设计人员能够提供有些操作的更有效实现。第18章将讨论如何使用右值引用来实现移动语义(move semantics)。以前的引用(使用&声明的引用)现在称为左值引用。

对象、继承和引用

        继承的一个特征是,基类引用可以指向派生类对象,而无需进行强制类型转化。这种特征的实际结果是,可以定义一个接受基类引用作为参数的函数,调用该函数时,可以将基类对象作为参数,也可以将派生类对象作为参数。

何时使用引用参数:

  • 程序员能够修改调用函数中的数据对象;
  • 通过产地引用而不是整个数据对象,可以提高程序的运行速度。

当数据对象较大时,如结构或类对象,第二个原因最重要。这些也是使用指针参数的原因。

什么时候使用引用、什么时候使用指针、什么时候使用值传递的指导原则:

对于使用传递的值而不做修改的函数:

  • 如果数据对象很小,如内置数据类型或小型结构,则按值传递。
  • 如果数据对象是数组,则使用指针,因为这是唯一个选择,并将指针声明为const的指针
  • 如果数据对象是较大的结构,则使用const指针或const引用,以提高程序的运行效率,这样可以节省复制结构所需的时间和空间。
  • 如果数据对象是类对象,则使用const引用。类设计的语义常常要求使用引用,这是C++新增这项特性的主要原因。因此,传递类对象参数的标注方式是按引用传递。

对于修改调用函数中数据的函数:

  • 如果数据对象是内置数据类型,则使用指针。如果看到如fixit(&x)这样的代码(其中x是int),则很明显,该函数将修改x。
  • 如果数据对象是数组,则只能使用指针。
  • 如果数据对象是结构,则使用引用或指针。
  • 如果数据对象是类对象,则使用引用。

8.3 默认参数

        通过函数原型可以设置默认参数,方法是将默认的值赋给原型中的参数。例如

char *left(const char *str, int n = 1);

left(str,2);
left(str);    // 等价于使用left(str,1);

        对于带参数列表的函数,必须从右向左添加默认值。也就是说,要为某个参数设置默认值,必须为它右边的所有参数提供默认值。请注意:当使用了函数原型设置默认参数后,函数的定义中不需要再次进行参数的默认值设置。

8.4 函数重载(函数多态)

        函数多态是C++在C语言基础上新增的功能。默认参数让您能够使用不同数目的参数调用同一个函数,而函数多态(函数重载)让您能够使用多个同名的函数。术语“多态”指的是有多种形式,因此函数多态运行函数可以有多种形式。术语“函数重载”值得是可以有多个同名的函数,因此对名称进行了重载。这两个术语指相同的事情,但我们通常使用函数重载。它用于完成相同的工作,但使用不同的参数列表。

        函数重载的关键是函数的参数列表——也称为函数特征标(function signature)。如果两个函数的参数数目和类型相同,同时参数的排列顺序也相同,则它们的特征标相同,而变量名是无关紧要的。C++允许定义名称相同的函数,条件是它们的特征标不同。如果参数数目或参数类型不同,则特征标也不同。一些看起来彼此不同的特征标是不能共存的。如:;编译器在检查函数特征标时,将把类型引用和类型本身视为相同的特征标。匹配函数时,并不区分是否为const常量。

double cube(double x);
double cube(double &x);

注意,是特征标不同使得可以函数重载,而不是返回值类型不同,倘若要得到不同类型返回值,则需要特征标也不同。

8.5 函数模板

        C++编译器实现了C++新增的一项特性——函数模板。函数模板是通用的函数描述,也就是说,它们使用泛型来定义函数,其中泛型可用具体的类型(如 int 或 double)替换。通过将类型作为参数传递给模板,可使得编译器生成该类型的函数。由于模板允许以泛型(而不是具体的类型)的方式编写程序,因此有时也被称为通用编程。由于类型是用参数表示的,因此模板特性有时也被称为参数化类型(parameterized types)

// 构造一个交换两个数据的值的模板
template <typename AnyType>
void swap(AnyType &a, AnyType &b){
    AnyType tmp;
    tmp = a;
    a = b;
    b = temp;
}
// 若仅仅涉及数字或单个char交换可以使用以下代码替换函数内部代码
a = a + b;
b = a - b;
a = a - b;

其中,第一行代码指出,要建立一个模板,并将类型命名为 AnyType(个人理解:类型的别名)。关键字 template 和 typename 是必须的,除非可以使用关键字 class 代替 typename 。林外必须使用尖括号。类型名可以任意选择(这里为 AnyType),主要遵守 C++ 命名规则即可;模板并不创建任何函数,而只是告诉编译器如何定义函数

        在程序中使用函数模板除了将上述代码中的函数声明和定义放在函数调用之前,也能够先进行函数声明(与普通函数声明类似,只不过要加上声明第一行代码指出的建立一个模板的代码)。

        注意:函数模板不能缩短可执行程序,也不能节省内存空间。对于使用了两个不同类型的同一个模板,最终仍然要生成两个独立的函数。最终代码不包含任何模板,而只包含了为程序生成的实际函数。使用模板的好处是,它使生成多个函数定义更简单、更可靠。

8.5.1 重载函数模板

        和重载函数一样,被重载的模板的函数特征标必须不同。并非所有的模板参数都必须是模板参数类型(typename T),而可以为具体类型(如int)。

8.5.2 模板的局限性

        模板类型不能将不同类型自动转换成相同类型。声明void TEST(T a, T b),调用char a;int b;
TEST(a, b);虽然char可以转换成int,但将依旧会报错:参数不明确,可能是char或int。

8.5.3 显式具体化(p287未理解)

        C++98 标志选择了以下的具体化方法:

  • 对于给定的函数名,可以有非模板函数、模板函数和显式具体化模板函数以及他们的重载版本。
  • 显式具体化的原型和定义应以 template<> 打头,并通过名称来指出类型。
  • 具体化优先于常规模板,而非模板函数优先于具体化和常规模板。

8.5.4 实例化和具体化(!!!这里也需要理解才行,待理解再补充)

        (个人理解:实例化是模板实例,具体化是将模板实例化生成函数)

        记住,在代码中包含函数模板本身并不会生成函数定义,它只是一个用于生成函数定义的方案。编译器使用模板为特定类型生成函数定义时,得到的是模板实例(instantiation)。模板并非函数定义,但使用int的模板实例是函数定义,这种实例化函数被称为隐式实例化(implicit instantiation)。

        最初,编译器只能通过隐式实例化来使用模板生成函数定义,但现在 C++ 还允许显示实例化(explicit instantiation),这意味着可以直接命令编译器创建特定的实例,如swap<int>()。其语法是,使用<>声明所需的种类,并在声明前使用关键字template:

template void swap<int>(int, int);

实现了这种特性的编译器看到上述声明后,将使用swa()模板生成一个使用int类型的实例。

        与显式实例化不同的是,显式具体化使用下面两个等价的声明之一:

template<> void swap<int>(int &, int &);
template<> void swap(int &, int &);

 区别在于:这些声明的意思是“不要使用swap()模板来生成函数定义,而应使用专门为int类型显式定义的函数定义”。这些原型必须有自己的函数定义。显式具体化声明在关键字 template 后包含<>,而显式实例化没有。

警告:试图在同一个文件中(或转换单元)中使用同一种类型的显式实例和显式具体化将出错。

还可以通过在程序中使用函数来创建显式实例化:

template<class T>
T Add(T a, T b)    // pass by value
{
    return a + b;
}
...
int m = 4;
double x = 9.0;
cout << Add(x, m);    // explicit instantiation

        这里的模板与函数调用 Add(x, m) 不匹配,因为该模板要求两个函数参数的类型相同。但通过使用 Add<double>(x, m), 可强制为double类型实例化,并将参数 m 强制转化为 double 类型, 以便于函数 Add<double> (double, double) 的第二个参数匹配。

第九章-单独编译、存储持续性、作用域、链接性、定位(placement)new运算符、名称空间

9.1 单独编译

        C++与C语言一样,允许甚至鼓励程序员将组件函数放在独立的文件中。可以单独编译这些文件,然后将它们链接成可执行文件。(通常,C++编译器既编译程序,也管理链接器)如果只修改一个文件,则可以只重新编译该文件,然后将它与其他文件的编译版本链接。可以将一个完整的程序分为以下三个部分:

  • 头文件:包含结构声明和使用这些结构的函数的原型;头文件包含了用户定义类型的定义;
  • 源代码文件:包含与结构有关的函数的代码;
  • 源代码文件:包含调用与结构相关的函数的代码

下面列出了头文件中常包含的内容:

  • 函数原型
  • 使用 #define 或 const 定义的符号常量
  • 结构声明
  • 类声明
  • 模板声明
  • 内联函数

        结构声明不创建遍历,只是在源代码文件中声明变量时,告诉编译器如何创建该结构变量。同样的,模板声明不是将被编译的代码,它们指示编译器如何生成与源代码中的函数调用相匹配的函数定义。被声明为 const 的数据和内联函数有特殊的链接属性,因此和可以将其放在头文件。

9.2 存储持续性、作用域和链接性

        C++11使用四种不同的方案来存储数据,这些方案的区别在于数据保留在内存的时间:

  • 自动存储持续性:在函数定义中声明的变量(包括函数参数)的存储持续性为自动的。他们在程序开始执行其所属的函数或代码块时创建,在执行完函数或代码块时,他们使用的内存被释放。C++ 有两种存储持续性为自动的变量。
  • 静态存储持续性:在函数定义外定义的变量和使用关键字 static 定义的变量的存储持续性都为静态。他们在程序整个运行过程中都存在。C++ 有3中存储持续性为静态的变量。
  • 线程存储持续性(C++11):如果变量是使用关键字 thread_local 声明的,则其生命周期与所属的线程一样长。
  • 动态存储持续性:用 new 运算符分配的内存将一种存在,直到使用 delete 运算符将其释放或程序结束为止。这种内存的存储持续性为动态,有时被称为自由存储(free store)或堆(heap)。

9.2.1 作用域和链接

        C++ 变量的作用域有多种。作用域为局部变量只在定义它的代码块中可用。代码块是由花括号括起来的一系列语句。

  • 作用域为全局(也叫文件作用域)的变量在定义位置到文件结尾之间都可以用。
  • 自动变量的作用域为局部,静态变量的作用域是全局还是局部取决于它是如何被定义的。
  • 在函数原型作用域(function prototype scope)中使用的名称只在包含参数列表的括号内可用(这就是为什么这些名称是什么以及是否出现都不重要的原因)。
  • 在类中声明的成员的作用域为整个类。
  • 在名称空间中声明的变量的作用域为整个名称空间(由于名称空间以及引入到C++语言中,因此全局作用域是名称空间作用域的特例)。

        C++ 函数的作用域可以是整个类或整个名称空间(包括全局的),但不能说局部的(因为不能再代码块内定义函数,如果函数的作用域为局部,则只对它自己是可见的,因此不能被其他函数调用。这样的函数将无法运行)。

9.2.2 自动存储持续性(普通常量)

        在默认情况下,在函数中声明的函数参数和变量的存储持续性为自动,作用域为局部,没有链接性。当程序开始执行这些变量所属的代码块时,将为其分配内存;当函数结束时,这些变量都将消失(注意,执行到代码块时,将为变量分配内存,但其作用域的起点为其声明位置)。如果在一个代码块中又有一个代码块,内部的代码块中有与外部的变量同名的,那么外部的变量将暂时不可见,在程序离开该代码块时,原来的定义又重新可见。

1、自动变量和栈

        程序使用两个指针来跟踪栈,一个指针指向栈底——栈开始的位置,另一个指针指向栈顶——下一个可用内存单元。新数据将被放在原有数据的上面(也就是说,在相邻的内存单元,而不是在同一内存单元)。当程序使用完后,将其从栈中删除。栈的默认长度取决于实现,但编译器通常提供改变栈长度的选项。当函数被调用时,其自动变量将被加入到栈中,栈顶指向变量后面的下一个可用的内存单元。函数结束后,栈顶指针被重置为函数被调用前的值(是重置而不是删除元素到原来函数未调用的位置),从而释放新变量使用的内存。

9.2.3 静态持续变量

        C++也为静态存储持续性变量提供了3中链接性:外部链接性(可在其他文件中访问)、内部链接性(只能在当前文件中访问)和无链接性(只能在当前函数或代码块中访问)。这3中链接性都在整个程序执行期间存在,与自动变量比,他们的寿命更长。由于静态变量的数目在程序运行期间是不变的,因此程序不需要使用特殊的装置(如栈)来管理他们。编译器将分配固定的内存块来存储所有的静态变量,这些变量在整个程序执行期间一直存在。另外如果没有显示的初始化静态变量,编译器将把它设置为0。在默认情况下,静态数组和结构将每个元素或成员的所有位置都设置为0。

        要创建链接性为外部的静态持续变量(即外部的全局变量),必须在代码块的外面声明它;要创建链接性为内部的静态持续变量(即内部的全局变量),必须在代码块的外面声明它,并使用 static 限定符;要创建没有链接性的静态持续变量,必须在代码内声明它,并使用 static 限定符所有的静态持续变量在整个程序执行期间都存在。所有的静态持续变量在未初始化时,所有位都被设置为0。这种变量被称为零初始化(zero-initialized)。

声明方式存储描述持续性作用域链接性
在代码块中自动自动代码块
在代码块中使用关键字register寄存器自动代码块
在代码块中使用关键字static静态,无链接性静态代码块
不在任何函数内部静态,外部链接性静态文件外部
不在任何函数内部,static修饰静态,内部链接性静态文件内部

零初始化和常量表达式初始化被统称为静态初始化,这意味着在编译器处理文件(翻译单元)时初始化变量。动态初始化意味着变量将在编译后初始化。

9.2.4 静态持续性、外部链接性(常规全局变量)

        链接性为外部的的变量通常简称为外部变量,他们的存储持续性为静态,作用域为整个文件。外部变量是函数外部定义的,因此对所有函数而言都是外部的。外部变量也称为全局变量(相对于局部的自动变量)。

1、单定义规则

        在每个使用外部变量的文件中,都必须声明它;变量只能有一次定义。为了满足这种需求,C++提供了两种变量声明。一种是定义声明(defining declaration)或简称为定义(definition),它给变量分配存储空间另一种是引用声明(reference declaration)或简称为声明(declaration),它不给变量分配存储空间,因为它引用已有变量。

        引用使用的关键字 extern,且不进行初始化;否则声明为定义,导致分配内存空间;如果要在多个文件中使用外部变量,只需要在一个文件中包含该变量的定义(单定义规则),但在使用该变量的其他所有文件中,都必须使用关键字 extern 声明它。

        注意:单定义规则并不意味着不能有多个变量的名称相同。例如在不同函数中声明的自动变量是彼此独立的,他们有自己的地址,局部变量可能隐藏同名的全局变量

        在C++提供了作用域解析运算符(::)。放在变量名前面时,该运算符表示使用变量的全局版本。通常情况下,应该使用局部变量,在需要时才传递数据,而不应该不加区分的使用全局变量来使数据可用。

9.2.5 静态持续性、内部链接性(静态全局变量)

        将 static 限定符用于作用域为整个文件的变量,该变量的链接性将内部的。在多文件程序,内部链接性和外部链接之间的差别:链接性为内部的变量只能在其所属的文件中使用;但在常规外部变量都具有外部链接性,即可以在其他文件中使用。

        如果在其他文件中使用相同的名称表示其他变量,省略关键字 extern 将报错,因为违反了单定义规则,而使用 static 来定义一个静态外部变量,静态变量将隐藏常规外部变量。

// file 1
int fox = 2;    // external declaration
...
// file 2;
int fox = 4;    // fail

注意:在多文件程序中,可以在一个文件(且只能在一个文件)中定义一个外部变量。使用该变量的其他文件必须使用关键字 extern 来声明它。

9.2.6 静态存储持续性、无链接性(静态局部变量)

        使用 static 限定符用于在代码块中定义的变量。在代码块中使用 static 时,将导致局部变量的存储持续性为静态的。这意味着虽然该变量只在该代码块中可用,但在该代码块不处于活动状态时仍然存在。因此在两次函数调用之间,静态局部变量的值将保持不变。(静态变量适用于再生)。另外,如果初始化了静态局部变量,则程序只在启动时进行一次初始化。以后再调用函数时,将不会像自动变量那样再次被初始化

9.2.7 说明符和限定符

        存储说明符:

  • auto ( 在C++11 中不再是说明符 );
  • regiter;
  • static;
  • extern;
  • thread_local ( C++11 新增的);
  • mutable。

        其中,除了 thread_local 能够和 static 和 extern 结合使用外,其他说明符不可以互相结合使用。 在 C++11 中,关键字 register 显示的指出变量是自动的。关键字 static 被用在作用域为整个文件的声明中时,表示内部链接性;被用于局部说明中,表示局部变量的存储持续性为静态的。关键字 extern 表明是引用声明,即声明引用在其他地方定义的变量。关键字 thread_local 指出变量的持续性与所属的线程的持续性相同。 thread_local 变量之于线程,犹如整个程序中的常规变量。关键字 mutable 的含义将根据 const 来解释。

1、cv-限定符( const 、volatile )

        const 表明,内存被初始化后,程序便不能再对它进行修改。

        volatile表明,即使程序代码没有对内存单元进行修改,其值也可能发生变化。例如可以将一个指针指向某个硬件位置,其中包含了来自串行端口的时间或信息。在这种情况下,硬件(而不是程序)可能修改其中的内容。或者两个程序可能相互影响,共享数据。关键字的作用是为了改善编译器的优化能力。例如,假设编译器发现,程序在几条语句中两次使用了某个变量的值,则编译器可能不是让程序查找这个值两次,而是将这个值缓存到寄存器中。这种优化假设变量的值在这两次使用之间不会变化。如果不将变量声明为 volatile ,则编译器将进行这种优化;将变量声明为 volatile ,相当于告诉编译器,不要进行这种优化。

2、mutable

        mutable用于指出即使结构(或类)变量为const 修饰的,其某个成员也可以被修改。

3、const 的默认存储类型

        在默认情况下,全局变量的链接性为外部的,但使用 const 修饰的全局变量的链接性是内部的。即和 static 修饰的全局变量相似。 利用这种特性可以将 const 修饰的常量放在头文件中。内部链接性意味着每个文件拥有属于自己的改名字的常量,而不是所有文件共享一组常量。

        使用 extern 关键字可以覆盖单用 const 修饰的变量的链接性( 从内部链接性变为了外部链接性 ),如下:

extern const int states = 12;    // definition with external linkage

在这种情况下,必须在所有使用该常量的文件中使用 extern 关键字来声明它。在函数或代码块中声明常量时,其作用域为代码块,即仅当程序执行该代码块中的代码时,该常量才是可用的。这意味着在函数或者代码块中创建常量时,不必担心其名称与其他定义的常量发生冲突。

9.2.8 函数和链接性

        和变量一样,函数也有链接性。和 C 语言一样,C++ 不允许在一个函数中定义另外一个函数,因此所有函数的存储持续性都自动为静态的,即在整个执行期间都一直存在。在默认情况下,函数的链接性为外部的,即可以在文件间共享。实际上可以在函数原型中使用关键字 extern 来指出函数是在另一个文件中定义的,不过这是可选的。还可以使用关键字 static 将函数的链接性设置为内部的,使得函数只能在一个文件中使用。必须同时在函数原型和函数定义中使用该关键字。其他文件便可以定义同名的函数,与变量一样,在定义静态函数的文件中,静态函数将覆盖外部定义。单定义规则也适用于非内联函数,因此对于每个非内联函数,程序只能包含一个定义,在多文件程序中,只能有一个文件包含该函数的定义,但使用该函数的每个文件都应该包含其函数原型。

        内联函数可以被放在头文件中,C++要求同一个函数的所有内联定义都必须相同。调用函数时,如果该文件中的函数原型指出该函数是 static 的,则编译器只在该文件中查找函数定义,否则,编译器(包括链接程序)将在所有的程序文件中查找。 如果知道两个定义,编译器将发出 ERROR ,因为每个外部函数只能有一个定义。如果在程序文件中没有找到,则编译器将在库中搜索。这意味着如果定义了一个与库函数同名的函数,编译器将使用程序员定义的版本。(即先查找函数原型是否为 static,然后查找程序文件,最后查找库)。

9.2.9 语言链接性(略)

9.2.10 存储方案和动态方案

        在 C++ 中使用运算符 new 或 C 函数 malloc() 分配的内存 被称为动态内存。 动态内存由运算符 new 和delete 控制,而不是由作用域和链接性规则控制。因此可以在一个函数中分配动态内存,而在另一个函数中将其释放。与自动内存不同,动态内存不是先进后出(LIFO),其分配和释放顺序取决于 new 和delete 在何时以何种方式被使用。通常编译器使用三块独立的内存:一块用于静态变量(静态存储区,可以细分),一块用于自动变量(栈),一块用于动态内存(堆)。

        注意:在程序结束时,由 new 分配的内存通常都将被释放,在不那么健壮的操作系统中,某些情况下,请求大型内存块将导致该代码块在程序结束不会被自动释放。最佳的做法是使用 delete 来释放 new 分配的内存。

        new 分配内存时,可能找不到请求的内存量,便会引发异常 std::bad_alloc.

定位new函数        p324(未看懂)

9.3 名称空间 namespace

        名称空间可以是全局的,也可以位于另一个名称空间中,但不能位于代码块中。因此默认情况下,在名称空间中声明的名称的链接性未外部的(除非它引用了常量)。

9.3.4 名称空间及其用途

        指导原则:

  • 使用在已命名的名称空间中声明的变量,而不是使用外部全局变量。
  • 使用在已命名的名称空间中声明的变量,而不是使用静态全局变量。
  • 如果开发了一个函数库或类库,将其放在一个名称空间中。事实上,C++ 当前提倡将标准函数库放在名称空间 std 中,这种做法扩展到了来自 C 语言中的函数。
  • 仅将编译指令 using 作为一种将旧代码转换为使用名称空间的权宜之计。
  • 不要再头文件中使用 using 编译指令。首先,这样做掩盖了要让哪些名称可用;另外,包含文件的顺序可能影响程序的行为。如果非要编译指令 using ,应将其放在所有预编译处理器编译指令 #include 之后。
  • 导入名称时,首选使用作用域解析运算符或 using 声明的方法。
  • 对于 using 声明,首选将其作用域设置为局部而不是全局。
  • 13
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值