C++高级编程(第4版)个人笔记 1.1 - C++基础知识

参考书籍:
《C++高级编程》(第4版)》
C++高级编程(第4版)个人笔记 1.1 - C++基础知识_第1张图片


1.1.1.2.预处理指令(P3)

  1. 生成一个C++程序共有三个步骤:
    1. 代码在预处理中运行,预处理器会识别代码中的元信息(meta-information);
    2. 代码被编译或转换为计算机可识别的目标文件;
    3. 独立的目标文件被链接在一起变成一个应用程序。
  1. 预处理指令以 #字符 开始,例如#include就是如此。
  2. C++ 中,声明通常放在扩展名为.h的文件中,称为头文件,其定义通常包含在扩展名为.cpp的文件中,称为源文件。许多其他编程语言(例如 C#Java)不把声明和定义放在不同的文件中。
  3. 注意:
    • 不使用.h后缀,改用前缀c;这是 新版本 ,也是推荐使用的版本。这些版本将一切放在std名字空间中(例如)。
    • 使用.h后缀,这是 旧版本。这些版本不使用名称空间(例如
  4. #ifndef的方式受C/C++语言标准支持。它不仅可以保证同一个文件不会被包含多次,也能保证内容完全相同的两个文件(或者代码片段)不会被不小心同时包含。
    当然,缺点就是如果不同头文件中的宏名不小心“撞车”,可能就会导致你看到头文件明明存在,但编译器却硬说找不到声明的状况——这种情况有时非常让人郁闷。
    由于编译器每次都需要打开头文件才能判定是否有重复定义,因此在编译大型项目时,ifndef会使得编译时间相对较长,因此一些编译器逐渐开始支持#pragma once的方式
    #pragma once 一般由编译器提供保证:同一个文件不会被包含多次。注意这里所说的 “同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。

1.1.1.3.main函数(P4)

  1. main()函数中,可以忽略显示的retrun语句;此时,会自动返回0。
  2. main()函数或没有参数,或者具有两个参数,如下所示:
    int main(int argc, char* argv[])
    argc给出了传递给程序的 实参书目,argv包含了这些 实参。注意argv[0]可能是程序的名称,也可能是空字符串,但不能依赖它。相反,应当使用特定于平台的功能来检索程序名。重要的是要记住:实际参数从索引 1 开始。

1.1.2.名字空间(P6)

  1. 警告:切勿在头文件中使用 using指令using 声明 ,否则添加你的头文件的每个人都必须使用它。
  2. C++17允许方便地使用嵌套的名字空间,即将一个名字空间放在另一个名字空间中。在C++17之前,必须按如下方式使用嵌套的名字空间:
namespace MyLibraties{
	namespace Networking {
		namespace FTP {
		/* ... */
		}
	}
}

这在C++17中得到极大简化:

namespace MyLibraries::Networking::FTP {
	/* ... */
}

可使用名字空间别名,为另一个名字空间指定一个更简短的新名称,例如:namespace MyFTP = MyLibraries::Networking::FTP

  1. C++提供了三种方法来显式地转换变量类型。第一种方法来自C,并且仍然被广泛使用,但实际上,已经不建议使用这种方法;第二种方法初看上去更自然,但很少使用;第三种方法最复杂,却最整洁,是推荐方法。
float myFloat = 3.14;
int i1 = (int)myFloat;	//方法1
int i2 = int(myFloat);	//方法2
int i3 = static_cast<int>(myFloat);	//方法3

当自动转换变量的类型时,应该了解潜在的数据丢失情况。

  1. ++运算符:一元运算符,使变量值增1。如果运算符在变量之后(后增量),表达式的结构是没有增加的值;如果运算符在变量之前(前增量),表达式的结果是增1后的值。
    int i = 1;
    cout << "i++: " << i++ << endl;
    cout << "i: " << i << endl;

    int j = 3;
    cout << "++j: " << ++j << endl;
    cout << "j: " << j << endl;
  • 显示结果:
    C++高级编程(第4版)个人笔记 1.1 - C++基础知识_第2张图片

1.1.7.条件语句(P12)

  1. C++17允许在if语句中包括一个初始化器,语法如下:
    if (; ) {body}
    中引入的任何变量只在 body 中可用。此类变量在if语句以外不可用。
    下面列出它的形式:
    if (Employee employee = GetEmployee(); employee.salary > 10000){...}
    在这个示例中,初始化器获得一名雇员,以及检索雇员的薪水是否超出 1000 的条件。只有满足调教才执行if语句体。

  2. 一旦找到switch条件匹配的case表达式,就执行其后的所有语句,直至遇到break语句为止。即使遇到另一个case表达式,执行也会继续,这称为 fallthrough 。下列有一组语句,会为把不同的case执行:

switch (backgroundColor) {
	case Color::DarkBlue:
	case Color::Black:
		//为深蓝色或黑色背景色执行的代码
		break;
	case Color::Red:
		//为红色背景色执行的代码
		break;
}
  1. 如果你无意间忘掉了break语句,fallthrough将成为bug的来源。因此,如果在switch语句中检测到fallthrough,编译器将生成警告信息(使用Jetbrains CLion未出现警告信息),除非像上例那样case为空。从C++17开始,你可以使用[[fallthrough]]特性,告诉编译器某个fallthrough是有意为之,如下所示:
switch (backgroundColor) {
	case Color::DarkBlue:
		doSomethingForDarkBlue();
		[[fallthrough]];
	case Color::Black:
		//为深蓝色或黑色背景色执行的代码
		doSomethingForBlackOrDarkBlue();
		break;
	case Color::Red:
	case Color::Green:
		//为红色或绿色背景色执行的代码
		break;
}
  1. 与if语句一样,C++17支持在switch语句中使用初始化器。语法如下
    switch (; ) {body}
    中引入的任何变量只在 body 中可用。此类变量在switch语句以外不可用。

1.1.9.函数(P15)

C++14允许要求编译器自动推断出函数的返回类。要使用这个功能,需要把auto指定为返回类型:

auto addNumbers(int number1, int number2)
{
	return number1 + number2;
}

编译器根据return语句使用的表达式推断返回类型。函数中可有多个return语句,但他们应解析为相同的类型。这种函数甚至可包含递归调用(调用自身),但函数中的第一个return语句必须是非递归调用。

1.1.10.C风格的数组(P16)

  1. 警告:在C++中,数组的第一个元素始终在位置0,而非位置1!数组的最后一个元素的位置始终是数组大小减1!
  2. 要设置基于栈的C风格数组的大小,可使用C++17 std::size()函数(需要 )。例如:
unsigned int arraySize = std::size(myArry);

1.1.11.std::array(P17)

array中,必须在尖括号中指定两个参数。第一个参数表示数组中元素的类型,第二个参数表示数组的大小。

array<int, 3> arr = {9, 8, 7};
cout << "Arry size = " << arr.size() << endl;
cout << "2nd element = " << arr[1] << endl;

显示结果:
在这里插入图片描述
注意:C风格的数组和std::array都具有固定的大小,在编译时必须知道这一点。在运行时数组不会增大或缩小。

1.1.13.结构化绑定(P18)

C++17引入结构化绑定(structured bindings)的概念,允许声明多个变量, 这些变量使用数组、结构、pair或元组中的元素来初始化。
例如,假设有下面的数组:

std::array<int, 3> values = {11, 22, 33};

可声明三个变量x、y和z,使用其后数组中的三个值进行初始化。注意,必须为结构化绑定使用auto关键字。例如,不能用int替代auto。

auto [x, y, z]= values;

使用结构化绑定声明的变量数量必须与右侧表达式中的值数量匹配。
如果所有非静态成员都是公有的,也可将结构化绑定用于结构。例如:

struct Point { double mX, mY, mZ;};
Point point;
point.mX  = 1.0; point.mY = 2.0; point.mZ = 3.0;
auto [x, y, z] = point;

1.1.14.循环(P18)

在循环中可使用 break 关键字立刻跳出循环并继续执行程序。关键字 continue 可用来返回到循环顶部并对while表达式重新求值。这两种风格都不提倡使用,因为它们会使程序的执行产生无规则的跳转,应该慎用。

1.1.15.初始化列表(P19)

初生始化列表在头文件中定义:利用初始化列表,可轻松地编写能接收可变数量参数的函数。initializer_list 类是一个模板, 要求在尖括号之间指定列表中的元素类型,这类似于指定vetor中存储的对象类型。下例演示如何使用初始化列表:

#include 
using namespace std;
int makeSum(initializer_list<int> lst)
{
    int total = 0;
    for (int value : lst) {
        total += value;
    }
    return total;
}

makeSum()函数接收一个整型类 型的初始化列表作为参数。函数体使用“基于区间的for 循环”来累加总数。可按如下方式使用该函数:

int a = makeSum({1, 2, 3}) ;
int b = makeSum({10, 20, 30, 40, 50, 60}) ;

初始化列表是类型安全的,会定义列表中允许的类型。对于此处的makeSum()函数,初始化列表中的所有元素都必须是整数。尝试使用double 数值进行调用,将导致编译器生成错误或警告,如下所示:

//在初始化列表中,不能将类型“ double”缩小为“ int”(已提供修复)
int c = makeSum({1, 2, 3.0});

在这里插入图片描述


欢迎关注公众号:c_302888524
发送:“C++高级编程(第3版)” 获取电子书
在这里插入图片描述

你可能感兴趣的:(c++)