整理C++基础与特性(10~?章,2022/1/7,P458)
本文最后更新于 1111 天前,其中的信息可能已经有所发展或是发生改变。

以《C++ Primer Plus》第六版为顺序整理,不排除中间插入某天突然学到的知识。对于每个知识点不会详解,需要详解的内容将会另开新章。

该文为此系列的第二篇;

第一篇:整理C++基础与特性(1~9章)

记录内容:

1、我不会或者不熟悉的

2、我认为重要的

3、C++11及以后新增的会在本文中提及,但详细会开新篇

第10章 对象和类

53 面向对象编程(OOP)

OOP特性:抽象、封装和数据隐藏、多态、继承、代码的可重用性;
结构的默认访问类型为public,而类为private;
通常将短小的类函数声明定义为内联函数;

54 类的构造函数和析构函数

类构造函数和析构函数没有返回值;

为了避免命名混乱,类成员名中通常使用m_作为前缀或_作为后缀;

构造函数:
显式调用构造函数:Stock food = Stock("World", 250, 1.25);
隐式调用构造函数:Stock food("World", 250, 1.25);

因为在构造函数构造出对象之前,对象是不存在的,因此构造函数不能通过对象来调用;
当且仅当没有定义任何构造函数时,编译器才会提供默认构造函数,但若为类定义了构造函数后就必须要提供默认构造函数,否则会报错,以此禁止创建未初始化的对象;

定义默认构造函数的方式有两种:
1 给已有构造函数的所有参数提供默认值:Stock(const string &co = "Error", int n = 0, double pr = 0.0};
2 通过函数重载来定义另一个没有参数的构造函数:Stock();
由于只能有一个默认构造函数,因此不能同时采用则两种方式;

析构函数:
如果构造函数没有使用new,则析构函数可以没有需要完成的工作,反之,需要在析构函数中使用delete;
在对象是用new创建的情况下,当使用delete释放内存时,其析构函数将会自动被调用;

C++11列表初始化:
在上篇中提及过,自然可应用于类:
Stock temp = {"A", 100, 1.5};
Stock temp{"A", 100, 1.5};
同时C++11提供了std::initialize_list的类,可将其用作函数参数或方法参数的类型,可表示任意长度的列表,只要所有列表项的类型都相同或可转换为相同的类型,后文将会详细介绍;

const成员函数:
const Stock land = Stock("A");
land.show();
针对上述代码,第二行无法确保调用对象不被修改而会产生错误,以往将函数参数声明为const引用或为指向const的指针来解决问题,但此处没有任何的参数,其使用的对象是由方法调用隐式提供的,故而需要用一种新的语法来处理:
void stock::show() const;
只要类方法不修改调用对象,就因将其声明为const;

其它:
接收一个参数的构造函数允许使用赋值语法来将对象初始化为一个值:Stock tubby = 32;
但这种特性可能会导致问题,后文会介绍如何关闭这一种特性;

55 this指针

当类成员函数需要涉及到两个对象,就需要使用this指针;
this指针指向用来调用成员函数的对象,其被作为隐层参数传递给函数,一般而言,所有的类方法都将this指针设置为调用它的对象的地址;
如果方法需要引用整个调用对象,可以使用*this;
在函数括号后哦面使用const可以将this限定为const;

56 作用域为类的常量

共有两种方法;

法一:
enum {months = 12};
double costs[months];

法二:
static const int months = 12;
double costs[months];

其中法二的常量将与其它静态变量存储在一起,而不是存储在对象中,被所有对象共享;

57 作用域内枚举(C++11)

传统的枚举中两个枚举定义的枚举量可能发生冲突,在C++11中提供了一种新枚举,即enum class;
其中可以用struct代替class,需要使用枚举名来限定枚举量;
默认情况下,C++11作用域内枚举的底层类型为int,另外还提供了新的语法,如下:
enum class : short pizza {small, medium, large, xlarge};
从而指定常规枚举的底层类型;

第11章 使用类

58 运算符重载

Time Time::operator+(const Time &t) const
{
  Time sum;
  sum.minutes = minutes + t.minutes;
  sum.hours = hours + t.hours + sum.minutes / 60;
  sum.minutes %= 60;
  return sum;
}
省略了类的定义(想必看得懂),以此举例;
其中运算符左侧的对象为调用对象,运算符右侧的对象为作为参数被传递的对象;

限制:
1 重载后的运算符必须至少有一个操作数是用户定义的类型,以防止用户为标准类型重载运算符;
2 使用运算符时不能违反运算符原来的句法规则,不能将求模运算符重载层使用一个操作数;
3 不能修改运算符的优先级;
4 不能创建新的运算符;
5 不能重载以下运算符:
  sizeof
  .成员运算符
  .*成员指针运算符
  ::作用域解析运算符
  ?:条件运算符
  typeid一个RTTI运算符
  const_cast强制类型转换运算符
  dynamic_cast强制类型转换运算符
  reinterpret_cast强制类型转换运算符
  static_cast强制类型转换运算符
6 以下运算符只能通过成员函数进行重载:
  =赋值运算符
  ()函数调用运算符
  []下标运算符
  ->通过指针访问类成员的运算符

59 友元(第15章将详细介绍)

友元函数:赋予该函数与类的成员函数相同的访问权限;
第一步:将其原型放在类声明中,并在原型声明前加上关键字friend;
 虽然函数是在类声明中声明的,但它不是成员函数,因此不能使用成员运算符来调用;
 虽然函数不是成员函数,但它与成员函数的访问权限相同
第二步:编写函数定义,因为不是成员函数,所以不能使用::限定符,同时不能再定义中使用friend;

第12章 类和动态内存分配

60 特殊成员函数

C++自动提供了下面这些成员函数:
·默认构造函数,如果没有定义构造函数
·默认析构函数,如果没有定义
·复制构造函数,如果没有定义
·赋值运算符,如果没有定义
·地址运算符,如果没有定义
此外,C++11还提供了移动构造函数和移动赋值运算符(18章);
带参数的构造函数也可以是默认构造函数,只要所有参数都有默认值;
复制构造函数在新建一个对象并将其初始化为同类现有对象时调用,最常见的情况时将新对象显式初始化为现有的对象
如StringBad *pStringBad = new StringBad(motto);使用motto初始化一个匿名对象,并将地址赋给pstring指针
具体地说,当函数按值传递时或函数返回对象时将使用复制构造函数
因此,应该使用按引用传递对象以节省调用构造函数的时间以及存储新对象的空间;
默认的复制构造函数逐个复制非静态成员(浅复制),复制的是成员的值,如果成员本身就是类对象,则将使用这个类的复制构造函数来复制成员对象。静态函数不受影响,因为它们属于整个类,而不是各个对象;

61比较成员函数

将比较函数作为友元,有助于将String对象与常规的C字符串进行比较,例如,假设answer是String对象,则下面的代码:
if("love"==answer)将被转换为if(operator==("love",answer))
然后,编译器将使用某个构造函数将代码转换为if(operator==(String("love),answer)),这与原型是相匹配的

62中括号表示法访问字符

在C++中,两个中括号组成一个运算符,即中括号运算符,可以使用operator[]()来重载该运算符。对于中括号运算符,一个操作数位于第一个中括号的前面,另一个操作数位于两个中括号之间,在表达式city[0]中,city是第一个操作数,[]是运算符,0是第二个操作数;
假设opera是一个String对象:
String opera("The Magic Flute");
则对于表达式opera[4],C++将查找名称和特征标与此相同的方法:
String::operator[](int i)
如果找到匹配的原型,编译器将使用下面的函数调用来替代表达式opera[4]:
opera.operator[](4)
opera对象调用该方法,数组下标4成为该函数的参数

参考资料:

  1. 《C++ Primer Plus》第六版
  2. 百度/谷歌
上一篇
下一篇