C++ C++ Primer 基础知识笔记

Posted baiiu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ C++ Primer 基础知识笔记相关的知识,希望对你有一定的参考价值。

第二章:变量和基本类型

列表初始化

C++语言定义了好几种不同形式的初始化,这也是初始化问题复杂性的一个体现。
C++11用花括号来初始化变量得到 了全面应用,这种初始化方式被称为列表初始化,现在无论是初始化对象还是赋新值,都可以使用花括号这种方式了。

int a = 0;
int a = {0};
int a{0};
int a(0);

默认初始化

如果定义变量时没有指定初值,则变量会被默认初始化。
内置类型是成员变量时会被初始化为0;是局部变量时将不初始化,是一个任意值,是一个不确定的值。
使用未初始化的变量将带来无法预计的后果;建议初始化每一个内置类 型的变量,可以提高程序的健壮性。

变量的声明和定义

为了支持分离式编译(即多文件,且每个文件可以独立编译),C++将声明和定义区分开来。

  • 声明是的名字为程序所知,一个文件如果想使用别的文件里定义的名字则必须包含对那个名字的声明;定义负责创建与名字关联的实体。

  • 变量能且只能被定义一次,声明可以被多次声明;
    变量的定义必须出现在且只能出现在一个文件中,而其他用到该变量的文件必须对其进行声明,而不能重复定义。

  • 任何包含了显示初始化的声明即为定义。

extern int i;  // 声明i而非定义i
int j; // 声明并定义了j
extern double pi = 3.14; // 定义

指针和引用

  • 指针本身就是一个对象,运行对指针赋值和拷贝;引用并非对象,它只是为一个已经存在的对象所起的另外一个名字;
  • 在指针的生命周期内它可以先后指向几个不同的对象
  • 指针无须在定义时赋初值,和其他内置类型一样,在块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值。
  • 引用本身不是一个对象,因此不能定义指向引用的指针;但指针的对象,所以存在对指针的引用;
int ival = 1024;
int &refVal = ival; // 引用refVal是ival的另一个名字
int i2 = refVal;
refVal = 2;
int &refVal3 = refVal;
int i = refVal3;
// i2是1024, i是2;


int *p = nullptr;  // p是一个指向i的的int类型指针
int *&r = p; // r是一个对指针p的引用,对指针的引用;

r = &i;  // r引用了一个指针,相当于 p = &i;
*r = 0; // 将i赋值为0

类型别名

使用 typedef 或 using 关键字来声明别名;

typedef  int Socket; // socket是int的别名
typedef double *p;  // p是double*的别名

using Socket = int;

第三章

数组

  • 数组声明如a[d];其中a是数组的名字,d是数组的维度。
    维度d应该在编译的时候就确定,即数组的个数d是一个常量表达式。

  • 默认情况下,数组内的元素会被默认初始化。

const unsigned int cnt = 42;
int arr[cnt]; // 正确

constexpr unsigned int size = get_size();
int arr2[get_size()];  // 正确,get_size()为常量表达式

int length = 42;
int arr3[length]; // 错误,length不是常量表达式
  • 显示初始化数组元素
int a[5] = {0, 1, 2};  // 等价于 a5 = {0, 1, 2, 0, 0};
string a2[3] = {"ni", "hao"};  // 等价于 a3 = {"ni", "hao", ""};
int a3[2] = {0, 1, 2}; // 错误,初始值太多

/*
 * 字符数组的特殊性
 */
char str1[] = {'c', '+', '+'};
char str2[] = {'c', '+', '+', '\\0'};  // 含有空字符的
char str3[] = "c++"; // 自动添加空字符
char str4[3] = "c++"; // 错误,没有空间存放空字符

第四章

左值和右值

当一个对象是右值的时候,用的是对象的值(内容);
当对象是左值的时候,用的是对象的身份(在内存中的位置)。

在使用左值和右值时候,一个重要的原则是:在需要右值的地方可以用左值来代替。当一个左值被当成右值使用时,实际使用的是它的内容(值)。

运算符

  • 取地址符作用于一个左值运算对象,返回一个指向该对象的指针,这个指针是一个右值;

  • 内置解引用运算符、下标运算符的求值结果都是左值;

  • 算数运算符的运算对象和求值结果都是右值;

  • 逻辑运算符和关系运算符的运算对象和求值结果都是右值;

  • 赋值运算符需要一个(非常量)左值作为其左侧运算对象,得到的结果也是一个左值;

  • 内置类型和迭代器的递增递减运算符作用于左值运算对象,前置版本将对象本身作为左值返回;后置版本则将对象原始值的副本作为右值返回。

  • 箭头运算符作用于一个指针类型运算对象,结果是一个左值;

  • 点运算符如果成员所属的对象是左值,那么结果是左值;反之则是右值;

  • 条件运算符的两个表达式都是左值或者能转换成同一种左值类型时,运算结果是左值;否则运算结构都是右值;

int i = 0, j = 0, k = 0; // 初始化而非赋值
const int ci = i;  // 初始化而非赋值

k = 0; // 赋值
i = j = k = 0; // 赋值运算满足右结合律;

隐式类型转换

int val = 3.5 + 3; // 精度丢失,隐式类型转换

有些类型转换是自动执行的,无须程序员介入和了解,因此被称为隐式转换;
比如在条件中,非布尔值转换成布尔类型

显示类型转换

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast

第六章

函数指针

  • 定义和使用
// 比较两个string对象的长度
bool lengthCompare(const string&, const string &);

// pf是一个函数指针,指向一个函数,该函数的参数是两个const string的引用
bool (*pf)(const string&, const string&);  // 未初始化

// 这是一个指针函数,返回bool*的函数
bool *pf(const string&, const string&); 


// 赋值函数指针,以下两条语句等价
pf = lengthCompare;
pf = &lengthCompare;

// 函数指针调用
pf("1","2");
(*pf)("1","2");
lengthCompare("1","2");
  • 函数指针作为形参
void useBigger(const string &s1, const string &s2, bool pf(const string&, const string &));
// 等价声明,第三个形参是函数类型,是一个函数指针
void useBigger(const string &s1, const string &s2, bool (*pf)(const string&, const string &));

以上是关于C++ C++ Primer 基础知识笔记的主要内容,如果未能解决你的问题,请参考以下文章

C++ C++ Primer 基础知识笔记

C++ Primer阅读笔记:基础

C++ Primer阅读笔记:基础

《C++ Primer Plus》学习笔记 第1章 预备知识

C++ Primer阅读笔记:vector|迭代器|数组

C++ Primer阅读笔记:vector|迭代器|数组