第12章 类和动态内存分配

1. 静态数据成员在类声明中声明,在包含类方法的文件中初始化。初始化时使用作用域运算符来指出静态成员所属的类。但如果静态成员是整形或枚举型const,则可以在类声明中初始化。

P426-P427类静态成员的声明和初始化

//strnbad.h

class StringBad

{

private:

static int num_strings;

};

//strnbad.cpp

int StringBad::num_strings = 0;

不能在类声明中初始化静态成员变量,这是因为声明描述了如何分配内存,但并不分配内存。请注意,初始化语句指出了类型,并使用了作用域运算符,但没有使用关键字static。初始化是在方法文件中,而不是在类声明文件中进行的,如果在头文件中,当头文件被包括在其他几个文件中时,将出现多个初始化副本,从而引发错误。

2. C++类自动提供了下面这些成员函数:

1)  默认构造函数,如果没有定义构造函数;

2)  默认析构函数,如果没有定义;

3)  复制构造函数,如果没有定义;

4)  赋值运算符,如果没有定义;

5)  地址运算符,如果没有定义。

C++11提供了另外两个特殊成员函数,移动构造函数和移动赋值运算符。

3. 带参数的构造函数也可以是默认构造函数,只要所有参数都为默认值。

4. 复制构造函数用于将一个对象复制到新创建的对象中。也就是说,它用于初始化过程(包括按值传递参数),而不是常规的赋值过程中。类的复制构造函数的原型通常如下:

Class_name(const Class_name &);

它接受一个指向类对象的常量引用作为参数。

5. 复制构造函数何时调用

新建一个对象并将其初始化为同类现有对象时,复制构造函数都将被调用,这包括当函数按值传递对象或返回对象。(函数按引用传递对象可以节省调用构造函数的时间以及存储新对象的空间)

6. 默认的复制构造函数逐个复制非静态成员(成员复制也称为浅复制),复制的是成员的值。静态成员不受其影响,因为它们属于整个类,而不是各个对象。

7. 如果类中包含了使用new初始化的指针成员,应当定义一个复制构造函数,以复制指向的数据,而不是指针,这称为深度复制。浅复制(成员复制)只是复制指针值。先复制只浅浅地复制指针信息,而不会深入“挖掘”以复制指针引用的结构。

8. 赋值运算符

将已有的对象赋值给另一个已有的对象时,将使用重载的赋值运算符。

初始化对象时,并不一定会使用赋值运算符。例如:

StringBad metoo = knot;

这里,metoo是一个新创建的对象,被初始化为knot的值,因此使用复制构造函数。然而实现时也可能会分两步来处理这条语句:使用复制构造函数创建一个临时对象,然后通过赋值将临时对象的值复制到新对象中。

9. 赋值运算符函数的实现和复制构造函数相似,但也有一些差别。

1)  由于目标对象可能引用了以前分配的数据,所以函数应使用delete[]来释放这些数据。

2)  函数应该避免将对象赋给自身;否则,给对象重新赋值前,释放内存操作可能删除对象的内容。

3)  函数返回一个指向调用对象的引用。

StringBad & StringBad::operator=(const StringBad & st)

{

if(this == &st)

return *this;

delete [] str;

len = st.len;

str = new char [len + 1];

strcpy(str, st.str);

return *this;

}

10. NULL是一个表示空指针的C语言宏。C++11引入新关键字nullptr,用于表示空指针。

11. P439-P440提供两个中括号表示法函数访问字符的原因

12. 静态类成员函数

可以将成员函数声明为静态的,函数声明必须包含关键字static,如果函数定义是独立的,则其中不能包含关键字static。这样做有两个重要的后果:

1)  首先,不能通过对象调用静态成员函数;而且静态成员函数不能使用this指针。如果静态成员函数是在公有部分声明的,则可以使用类名和作用域解析运算符来调用它。

2)  由于静态成员函数不与特定的对象相关联,因此只能使用静态数据成员。

13. P449——P451

有关返回对象的说明

如果方法或函数要返回局部对象,则应返回局部对象,而不是指向对象的引用。在这种情况下,将使用复制构造函数来生成返回的对象。如果方法或函数要返回一个没有公有复制构造函数的类(如ostream类)的对象,它必须返回一个指向这种对象的引用。最后,有些方法或函数(如重载的赋值运算符)可以返回对象,也可以返回指向对象的引用,在这种情况下,应该首选引用,因为其效率更高。

14. 如果对象是用new创建的,则仅当您显式使用delete删除对象时,其析构函数才会被调用。

15. P456-P459显式地为使用定位new运算符创建的对象调用析构函数。

16. ostream & operator<<(ostream & os, const c_name & obj)

{

os << …;

return os;

}

c_name是类名。如果该类提供了能够返回所需内容的公有办法,则可在运算符函数中使用这种方法,这样便不用将它们设置为友元了。

17. 嵌套的结构和类

在类声明中声明的结构、类或枚举被称为是被嵌套在类中,其作用域为整个类。这种声明不会创建数据对象,而只是指定了可以在类中使用的类型。如果声明是在类的私有部分进行的,则只能在这个类使用被声明的类型;如果声明是在公有部分进行的,则可以从类的外部通过作用域解析运算符使用被声明的类型。

18. 成员初始化列表

从概念上讲,调用构造函数时,对象将在括号中的代码执行之前被创建(成员变量被分配内存),然后,程序进入到括号中,使用常规的赋值方式将值存储到内存中。对于const类成员和被声明为引用的类成员,必须在构造函数中使用初始化列表,使它们在创建时进行初始化。对于其它简单数据成员,使用初始化列表和在函数体中赋值没有什么区别。对于本身就是类对象的成员来说,使用成员初始化列表的效率更高,因为它直接使用复制构造函数,减少了再调用赋值运算符函数这个步骤。

语法:

Classy::Classy(int n, int m) : mem1(n), mem2(0), mem3(n * m + 2)

{

}

总之,请注意以下几点:

1)  这种格式只能用于构造函数;

2)  必须用这种格式来初始化非静态const数据成员;

3)  必须用这种格式来初始化引用数据成员。

数据成员被初始化的顺序与它们出现在类声明中的顺序相同,与初始化器中的排列顺序无关。

19. C++11的类内初始化

C++11允许您以更直观的方式进行初始化:

class Classy

{

int mem1 = 10;

cons tint mem2 = 20;

}

这与在构造函数中使用成员初始化列表等价。

如果同时使用了成员初始化列表的构造函数,则列表将覆盖这些默认初始值。

20. 伪私有方法:

如果类目前不需要提供复制构造函数和赋值运算符,然而将来的时候可能需要复制对象,您可能会忘记没有为复制提供适当的代码,在这种情况下,程序能够编译和运行,但结果确实混乱的,甚至会崩溃。这时候可以使用伪私有方法。

class Queue

{

private:

Queue(const Queue & q){}

Queue & operator=(const Queue & q){return *this}  …

}

有两个作用:第一,避免了本来将自动生成的默认方法定义。第二,因为这些方法是私有的,所以不能被广泛使用。

Queue nick(nip);   //not allowed

tuck = nip;         //not allowed

所以,与其将来面对无法预料的运行故障,不如得到一个易于跟踪的编译错误,指出这些方法是不可访问的。另外,在定义其对象不允许被复制的类时,这种方法也很有用。

C++ primer plus读书笔记——第12章 类和动态内存分配的更多相关文章

  1. 《C++ Primer Plus》第12章 类和动态内存分配 学习笔记

    本章介绍了定义和使用类的许多重要方面.其中的一些方面是非常微妙甚至很难理解的概念.如果其中的某些概念对于您来说过于复杂,也不用害怕——这些问题对于大多数C++的初学者来说都是很难的.通常,对于诸如复制 ...

  2. C++ primer plus读书笔记——第13章 类继承

    第13章 类继承 1. 如果购买厂商的C库,除非厂商提供库函数的源代码,否则您将无法根据自己的需求,对函数进行扩展或修改.但如果是类库,只要其提供了类方法的头文件和编译后的代码,仍可以使用库中的类派生 ...

  3. 《C++ Primer 4th》读书笔记 第12章-类

    原创文章,转载请注明出处:http://www.cnblogs.com/DayByDay/p/3936473.html

  4. 《C++ Primer Plus》读书笔记之十—类和动态内存分配

    第12章 类和动态内存分配 1.不能在类声明中初始化静态成员变量,这是因为声明描述了如何分配内存,但并不分配内存.可以在类声明之外使用单独的语句进行初始化,这是因为静态类成员是单独存储的,而不是对象的 ...

  5. JVM学习笔记-第三章-垃圾收集器与内存分配策略

    JVM学习笔记-第三章-垃圾收集器与内存分配策略 tips:对于3.4之前的章节可见博客:https://blog.csdn.net/sanhewuyang/article/details/95380 ...

  6. C++ primer plus读书笔记——第17章 输入、输出和文件

    第17章 输入.输出和文件 1. 对键盘进行输入缓冲可以让用户在将输入传输给程序之前返回并更正.C++程序通常在用户按下回车键时刷新输入缓冲区. 2. 一些I/O类 streambuf类为缓冲区提供了 ...

  7. C primer plus 读书笔记第四章

    本章的标题是字符串的格式化输入/输出,重点介绍输入和输出. 本章的第一段示例代码和上一张示例代码很相近,代码就不贴了,新出现的特性是使用了一个数组来存放字符串,C预处理命令和strlen()函数. 下 ...

  8. C++ primer plus读书笔记——第16章 string类和标准模板库

    第16章 string类和标准模板库 1. string容易被忽略的构造函数: string(size_type n, char c)长度为n,每个字母都为c string(const string ...

  9. C++ primer plus读书笔记——第15章 友元、异常和其他

    第15章 友元.异常和其他 1. 友元类的所有方法都可以访问原有类的私有成员和保护成员.另外,也可以做更严格的限制,只将特定的成员函数指定为另一个类的友元.哪些函数.成员函数.或类为友元是由类定义的, ...

随机推荐

  1. c++ 反汇编 循环结构

    debug do···while 23: int nSum = 0; 00A572AE C7 45 F8 00 00 00 00 mov dword ptr [nSum],0 24: int nInd ...

  2. python3 虚拟环境 venv

    创建一个虚拟环境: python -m venv test            (test 为创建的虚拟环境目录) 激活虚拟环境: test\Scripts\activate            ...

  3. Web 前端 - 又不仅限于 Web 前端 - 协程锁问题

    前言 最近两天的 web 前端开发中,早前的锁实现 (自旋锁) 出现了一些不合理的现象,所以有了这片随笔 什么是协程锁?能点进这个博客的的你肯定是明白的,不明白的人根本搜不到我这随笔,不多做赘述. 一 ...

  4. java 基础知识储备

    初始JAVA JAVA 帝国的诞生 1972年C诞生 贴近硬件,运行极快,效率极高. 操作系统,编译器,数据库,网络系统等 指针和内存管理 1982年C++诞生 面向对象 兼容C 图形领域.游戏等 纵 ...

  5. Redis生产环境节点宕机问题报错及恢复排错

    Redis故障发现 主观下线 当cluster-node-timeout时间内某节点无法与另一个节点顺利完成ping消息通信时,则将该节点标记为主观下线状态. 客观下线 当某个节点判断另一个节点主观下 ...

  6. flexbox(弹性盒布局模型),以及适用场景

    一.是什么 Flexible Box 简称 flex,意为"弹性布局",可以简便.完整.响应式地实现各种页面布局 采用Flex布局的元素,称为flex容器container 它的所 ...

  7. C++中的广义集合于for范围访问

    在C++11中可以通过for在范围循环中访问广义集合 如: std::vetcor v={1,2,3}; int a[4] {1,2,3,4}; std::array<int,4>a2 { ...

  8. 树结构系列(三):B树、B+树

    树结构系列(三):B树.B+树 文章首发于「陈树义」公众号及个人博客 shuyi.tech,欢迎访问更多有趣有价值的文章. 文章首发于「陈树义」公众号及个人博客 shuyi.tech 平衡二叉树的查找 ...

  9. spring5源码编译过程中必经的坑

    spring源码编译流程:Spring5 源码下载 第 一 步 : https://github.com/spring-projects/spring-framework/archive/v5.0.2 ...

  10. Horovod-Usage

    Usage 代码中要包含以下6步: 初始化 Run hvd.init() to initialize Horovod. 将每个GPU固定到单个进程以避免资源争用. 一个线程一个GPU,设置到 loca ...