【笔记】【第1天】C++学习之路

Witheart... Follow your heart...

目录

前言

学习内容

string 类

与字符数组的相同点

与字符数组的不同点

对 string 类的其他操作

string 类 I/O

关于友元函数

原始(raw)字符串(C++11)

结构体

声明结构变量

结构体赋值

结构中的位字段

共用体(union)

普通共用体

匿名共用体(anonymous)

枚举(enum)

枚举的赋值与运算

使用枚举进行符号常量的定义

枚举量的值

关于OOP

关于指针

'*'

这是我目前看过的关于指针变量声明的最好解释

野指针

指针的赋值和运算

new

不同内存区域

new 失败

delete

new 一个动态数组

今日学习 9 小时,明天继续加油!

共勉


前言

        本系列文章作为笔者学习C++的一个记录,希望可以坚持下去。目前使用C++Primer Plus这本书。

        本系列文章适合有C语言基础的读者阅读。

学习内容

  • string 类

        要使用 string 类,应该在程序中包含头文件 string。

与字符数组的相同点

        string 对象的很多使用方法和使用字符数组是相同的,如下

string str = "nnn";

cin >> str;

cout << str;

cout << str[2];
  1. 可以使用字符串常量初始化 string 对象;
  2. 可以使用 cin 和 cout 操作 string 对象;
  3. 可以使用下标表示 string 对象中的字符。

与字符数组的不同点

  1. 可以使用赋值号 "=" 将一个 string 对象赋值给另外一个 string 对象;
  2. 可以使用运算符 "+" 将一个 string 对象加到另外一个 string 对象的末尾;
str1 = str2;
str3 = str1 + str2;

对 string 类的其他操作

确定 string 类字符串中字符数的方法

int len1 = str1.size();

        str1 是一个对象,而 size() 是一个类方法。方法是一个函数,只能通过其所属类的对象进行调用。

string 类 I/O

        对于字符数组,常见的输入程序是:

cin.getline(charray, 20);

        cin 是一个 istream 对象,使用句点表示法表明,函数 getline() 是 istream 类的一个类方法。

        而对于 string 对象,常见的输入程序是:

getline(cin, str);

        这里没有使用句点表示法,表明这个 getline() 不是类方法。它使用 cin 作为参数,指出到哪里查找输入。

关于友元函数

        为什么一个 getline() 是 istream 的类方法而另一个不是呢?

        这是因为,在引入 string 类之前,C++早就有 istream 类,因此 istream 类的设计考虑了其他如 int、double等变量类型,但是没有考虑 string 类,所以 istream 类中没有处理 string 的类方法。

        但是为什么下面的代码可行呢?

cin >> str;

        像 "cin >> x" 使用了istream 的一个成员函数(此处 x 不为 string 类),而 cin >> str 使用 string 类的一个友元函数。

原始(raw)字符串(C++11)

        在原始字符串中,字符表示的就是自己,如  \n 不表示换行符,而是表示斜杠和 n,避免了转义的麻烦。

        原始字符串用括号 ( ) 界定字符串范围,并且使用前缀 R 来标识原始字符串,如:

cout << R"(I love NNN.\n)";    //将显示I love NNN.\n

        而输入原始字符串时,按回车不仅会使光标移动到下一行,还将在原始字符串中添加回车字符。

        有个问题是如何在原始字符串中包含 "(" 或者 ")"。实际上,当编译器遇到 ")" 时就会认为字符串到此结束,但是为了可以在字符串中添加 ")",原始字符串允许自定义原始字符串的界定标志,就是在字符串开头的 " 和 ( 之间添加其他字符,结束时也必须添加这些字符。如:

cout << R"+++(I'm almost asleep. (That is real.))+++" << endl;

//将显示如下面的内容
I'm almost asleep. (That is real.)

        此时定界符变成了 "+++( 和 )+++"。

  • 结构体

声明结构变量

        与C的差别是,在C++中,定义结构后,声明结构变量时可以省略关键字 struct,如

struct nnn{
    int a;
    int b;
};

nnn n1;    //定义了 nnn 类型的结构体 n1

结构体赋值

        结构体可以使用 "=" 将一个结构体赋值给另外一个结构体,即使成员有数组,这种赋值被称为成员赋值(memberwise assignment)。

结构中的位字段

        与C语言一样,C++也允许使用占用特定位数的结构体成员,这种用法适用于创建与某个硬件上的寄存器相对应的数据结构。

struct torgle_register{
    unsigned int SN : 4;    //4 bits for SN value
    bool goodIn : 1;        //1 bit for valid input
}

  • 共用体(union)

普通共用体

        不同时间,可以存储其中定义的不同数据类型;同一时间,只能同时存储一种数据类型。使用方法如下:

union one4all    //共用体声明    
{
    int int_val;
    long long_val;
    double double_val;
}

one4all pail;    //此时,pail 可以在不同的时间存储不同类型的变量

pail.int_val = 5;
pail.double_val = 2.5;

        当同一种数据的类型可能有多种选择时,可以使用共用体。

匿名共用体(anonymous)

        匿名共用体没有名称,其成员为位于相同地址处的变量,使用时无需添加名称即可使用。

枚举(enum)

        可以用来创建符号常量,代替 const。定义方法如下:

enum color {red, green, blue, yellow};    //定义 color 为一种新的枚举类型名称

color color1;    //使用枚举名来声明这种枚举类型的变量

        red, green, blue, yellow 被称为符号常量,对应整数值 0~3。

枚举的赋值与运算

  • 不进行强制类型转换时,一个枚举变量只能由定义它所属的枚举类型时的符号常量赋值;
color1 = red;    //有效,red 是该枚举变量所属的符号常量
color1 = 0;      //无效,0 是整数值
  • 枚举变量只能进行赋值,不能进行算术运算;
  • 但是,枚举类型可以被提升为 int 类型,但 int 类型不能自动转换为枚举类型,在一些实现中,使用枚举类型进行算术运算时,枚举类型将被自动转换为 int 类型后进行算术运算。
  • 使用强制类型转换对枚举变量进行赋值时,只要这个被强制转换的量在枚举的取值范围内(range),就可以成功赋值,即使该值不是枚举值,如:
enum bits {one = 1, two = 2; four = 4; eight = 8};
bits myflag;

myflag = bits(6);    //对 6 进行强制类型转换,并赋值给枚举变量 myflag,且该取值是有效的

        6 并不是枚举值,但是该赋值的操作是合法有效的,这是为什么呢?

        这是因为 6 处于该枚举定义的取值范围内。取值范围定义如下:

上限:大于枚举量的最大值的最小的 2 的幂减 1(上面的枚举的上限就为 16 - 1 = 15);

下限:最小值不小于 0 时,下限为0,否则计算方式与上限的计算方式一致,但是要加上负号。

使用枚举进行符号常量的定义

        可以省略枚举类型的名称,如下:

enum {red, green, blue, yellow}; 

枚举量的值

        定义枚举类型时,可以显式地定义其中一些枚举量的值,后一个枚举量的值比前一个大 1。

        一个枚举类型中,不同枚举量的值可以相同。

关于OOP

        强调的是在运行阶段(而不是编译阶段)进行决策,根据程序运行时用户的不同行为进行决策。比如用户输入多大的字符串就在程序运行时为其分配多大的空间存放字符串,而不是在编译的时候预先分配空间,这样可能在实际情况中遇到分配的空间过大造成浪费或者分配空间过小造成错误。

关于指针

'*'

        *运算符被称为间接值(indirect value)运算符或解除引用(dereferencing)运算符,应用于指针时,可以得到该地址所存储的值。

这是我目前看过的关于指针变量声明的最好解释

int * p;

上面的代码表明 *p 是一个 int 型变量,而 * 只能应用于指针,所以 p 的类型是指向 int 的指针,也称为 int*。

注意,连续声明指针变量时,在每个指针变量前都要使用 * ,如下:

int *p1, *p2, p3;

此时 p1、p2 都是指向 int 的指针,而 p3 是 int 型变量。

初始化指针时,使用取地址符 &,如下:

int* p = &nnn;

野指针

对指针应用解引用运算符 * 之前,一定要先使其指向一个适当的地址!

指针的赋值和运算

指针的意义是地址,两个指针相乘没有意义;而整数可以相乘。这说明指针和整数不是完全相同的,所以不能简单地将整数赋值给指针,而应该使用强制类型转换。

new

类似于 malloc(),但是 new 运算符更好用。示例:

int* p = new int;

        new int 告诉程序需要适合存储 int 的内存,然后 new 分配好内存后返回首地址。

不同内存区域

普通变量存储于栈(stack)中,而 new 在堆(heap)中分配变量。堆也叫自由存储区(free store)。

new 失败

内存不足导致 new 请求失败时,new 通常会引发异常;而在较老的实现中,new 将返回 0,也就是空指针(null pointer),空指针不会指向有效的数据,所以常用来表示失败。

delete

当使用 new 请求的内存使用完毕后,应该使用 delete 释放内存(free)。使得该内存可以被重新利用,用法如下:

int* p = new int;
..

delete p;

这将释放 p 指向的内存,但是不会删除 p 指针本身,可以使其重新指向新的变量。

        new 与 delete 一定要配对使用,否则被分配的内存无法再次被使用,这被称为内存泄漏(memory leak),内存泄漏严重时,程序将由于内存不足而终止。

        不要使用 delete 尝试释放已经释放的内存块,这样的结果是未定义的。

        使用 delete 释放声明变量所获得的内存是非法的。

        注意,delete 本质上是作用于 new 分配的内存,所以并不一定要与用于 new 的指针一起使用,而是用于 new 分配的内存地址,如下:

int* p1 = new int;
int* p2 = p1;
..

delete p2;    //使用 p2 释放 new 分配的内存

        可以对空指针使用 delete,这是安全的。

new 一个动态数组

静态联编(static binding):在编译时给数组分配内存

动态联编(dynamic binding):在程序运行时选择数组的长度(这种数组被称为动态数组(dynamic array))

动态数组创建与释放

new 时,在类型名后加上方括号,其中包含元素数目,如下:

int* p = new int [10];

delete时,在指针和 delete 之间也应该有方括号,如下:

delete [] p;

        方括号告诉程序应该释放整个数组的内存而不仅仅时指针指向的元素;

        new 和 delete 格式要匹配,即使用 new 时不带方括号,那么 delete 时也不带方括号,以此类推。格式不匹配导致的后果是不确定的。

        不能用 sizeof 运算符来确定动态分配的数组包含的字节数。

动态数组使用

        只要把指针当作数组名使用即可。可以使用方括号下标表示法,如 p[0]。

数组名与指针

        数组名和指针的差别是:

  1. 数组名是常量,值是不能修改的;指针是变量,可以修改它的值;
  2. 对数组名应用 sizeof 运算符得到的是数组的长度,而对指针应用 sizeof 运算符得到的是指针的长度,即使指针指向的是一个数组;
  3. 对数组取地址时,数组名不会被看作时地址。实际上,数组名被解释为第 1 个元素的地址,而对数组名应用 &地址运算符时,得到的是整个数组的地址。前者是数组中单个元素的内存块的地址,后者是整个数组的内存块地址。如下:
short tell[10];

        tell 的类型是 short*,而 &tell 的类型是 short(*)[10] (指向包含 10 个 short 元素数组的指针)。

        类似于 short(*)[10] 这种类型的由来:

short (*p)[10] = &tell;

        这种声明的括号必不可少,由于优先级规则,缺少括号时 p 将先和 [10] 结合,导致 p 中的元素都是 short* 型的,p 是一个指针数组,包含 10 个 short 型指针。

        要描述 p 的变量类型时,可以删去声明中的变量名,所以 p 的类型为 short(*)[10]。

        此外,由于 p 被赋值为 &tell,因此 *p 和 tell 等价,所以可将 (*p)视为数组名进行操作。

今日学习 9 小时,明天继续加油!

共勉

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__Witheart__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值