《C++ Primer Plus》 学习笔记-第四章

1、数组(array)

  • 数组声明:type name[10];注意:数组下标是从0开始,即0、1、2…9。
  • 数组的初始化规则:
  • 数组大小int a[x] x不能是变量(const 或者 固定),但可以使用new解决限制
//只有定义数组时才能使用声明
float a[3] = {1,2,3};		//ok
float b[3];					//ok
b[3] = {1,2,3}				//not allowed
b = a;							//not allowed
//若只对一部分进行初始化,则其他为0
int a[500] = {};	//全为 0
int a[500] = {0};
int a[500] = {1};
int a[500] = {1,3};
//[]内为空,自动计算其大小
short a[] = {1,2,3,4};
//c++11中初始化
double a[] = {1,2,3,4};
//列表初始化禁止缩窄转换
  • 数组替代:C++模板类 array vector(后续会有)

字符串

char data[] = {'a','a',' ','a','\0'};		//检测到空字符\0即停止
char data[] = "HAHAHHAHAHA";			//隐式包含结尾的空字符\0
cout<<"a b c " "d e f";					//拼接字符串常量
include <cstring>				//char 的string
const int Size = 15;
char name1[Size] ;
char name2[Size] = "C++ppp";
strlen(name2);	//字符长度
sizeof(name2);	//数组长度
cin>>name1;
name[3] = '\0';		//截断name2
//name1显示cin,name2显示C++;
  • cin的控制输入 空格即为 \0
    cin.get()是保留回车在输入流队列中的,
    而cin是丢弃回车的。
  • 混合使用 cin>> 和 cin.get
    将 cin >> 与 cin.get 混合使用可能会导致烦人且难以发现的问题。请看下面的代码段示例:
char ch;    //定义一个字符变量
int number; //定义一个整型变量
cout << "Enter a number:;
cin >> number;   // 读取整数 丢弃换行符
cout << "Enter a character: ";
ch = cin.get() ;   // 读取字符 包含换行符
cout << "Thank You!\n";

这些语句允许用户输入一个数字,而不是一个字符。看来第 6 行的 cin.get 语句已经被跳过了。这是因为 cin>> 和 cin.get 使用略有不同的技术来读取数据。
在示例代码段中,当执行第 4 行时,用户输入一个数字,然后按回车键。假设输入的是数字 100。按回车键会导致一个换行符(’\n’)存储在键盘缓冲区数字 100 之后,如图

cin.get和cin
第 4 行中的 cin>> 语句读取用户输入的数据时,它会在遇到换行符时停止。换行字符未被读取,而是仍保留在键盘缓冲区中。从键盘读取数据的输入语句只在键盘缓冲区为空时等待用户输入值,但现在不为空。
cin使用空白(空格、制表符和换行符)来确定字符串的结束位置,cin在获取字符数组时只读取一个单词。

  • 面向行的输入getline()
    通过回车键输入的换行符来确定输入结尾。但不保存换行符,而是用空字符来替换换行符。
    cin.getline(name,20);
    第一个参数是数组的名字,第二个参数是要读取的字符数。20代表最多读取19个字符,剩下的用于存储自动添加的空字符。
  • 面向行的输入get()
    get与getline类似,但是不读取并丢弃换行符,而是将其留在队列中。
    使用不带任何参数的cin.get()调用读取下一个字符,还可拼接cin.get(name,20).get();
    使用get()时检查停止读取的原因,查看下一个字符,若为换行符,则说明读取了整行。
    读取空行时,使用cin.clear();恢复输入。
  • 混合输入字符串和数字
    cin读取数字时,将回车键生成的换行符留在了队列中,再使用cin.getline(),将会认为是空行,使用cin.get()解决。
    P80页
cin >> year ;
//添加一行  cin.get();  读取掉换行符
cin.getline(a,20);

string类

include <string>
string str1 ;
string str2 = "hahahhaha";
cin>>str1;		//str1创建一个长度为0的string对象,但读取输入之后,将自动调整str1的长度。
string str = str1 +str2;
cout<<str1.size();		//string类的size函数
getline(cin,str1);		//string类中的getline函数 ,读取一行

C++11新的字符串类型

  • wchar_t、char16_t、char32_t
//三种类型分别加前缀 L,u,U 表示
wchar_t a[] = L"Chief";
char16_t a[] = u"hahha";
char32_t a[] = U"asaaaaaaaaaaaaaaa";
  • raw原始字符串:用R"( )"标识
    cout<<R"(hAHHHH "\n" )" 不会转义;

2、结构

(例子:球队成员名字,身高体重等)

#include <iostream>
#include <string>
using namespace std;
struct product 
{
	char name[10];
	string description;
	float volum;
	double price;
};
int main()
{
	product a =
	{
		"aaa","111",12,45
	};
	product b =
	{
		"bbb","222",23,545
	};
	cout << a.name << b.name << endl;
	getchar();
	return 0;
}

共用体

是一种数据格式,但是在某一句话中只能存储一种类型的数据

union exam
{
	int a;
	double b;
}
exam.a = 15;
exam.b = 332.3;		//int丢失

枚举

  • enum是一种新的创建符号常量的工具,可以替代const,还允许定义新类型
    enum spectrum(red=0, orange, yellow, green, blue, violet, indigo, ultraviolet)
    int i = red;
    spectrum:(枚举)enumeration
    red等称为枚举量 :对应为0-7
  • 枚举常量代表该枚举类型的变量可能取的值,编译系统为每个枚举常量指定一个整数值,默认状态下,这个整数就是所列举元素的序号,序号从0开始。 可以在定义枚举类型时为部分或全部枚举常量指定整数值,在指定值之前的枚举常量仍按默认方式取值,而指定值之后的枚举常量按依次加1的原则取值。 各枚举常量的值可以重复。
  • 枚举常量只能以标识符形式表示,而不能是整型、字符型等文字常量
enum letter_set {'a','d','F','s','T'}; //枚举常量不能是字符常量
enum year_set{2000,2001,2002,2003,2004,2005}; //枚举常量不能是整型常量

3、指针和自由存储空间

指针与C++基本原理

面向对象编程与传统的过程性编程的区别在于,OOP强调的是在*运行阶段(而不是编译阶段)进行决策。运行阶段指的是程序正在运行时,编译阶段指的是编译器将程序组合起来时。运行阶段决策就好比度假时,选择参观那些景点取决于天气和当时的心情;而编译阶段决策更像不管在什么条件下,都坚持预先设定的日程安排。
运行阶段决策提供了灵活性,可以根据当时的情况进行调整。例如,考虑为数组分配内存的情况。传统的方法是声明一个数组。要在C+中声明数组,必须指定数组的长度。因此,数组长度在程序编译时就设定好了;这就是编辑阶段决策。虽有可能在80%的情况下,一个包含20个元素的数组足够了,但程序有时需要处理200个元素。为了安全起见,使用了一个包含200个元素的数组。这样,程序在大多数情况下都浪费了内存。OOP通过将这样的决策推迟给运行阶段进行,使程序更灵活。在程序运行后,可以这次告诉它只需要20个元素,而且还可以下次的时候告诉它需要205个元素。
总之,使用OOP时,您可以在运行阶段确定数组的长度。为了使用这种方法,语言必须允许在程序运行时创建数组。C++采用的方法是,使用关键字new请求正确数量的内存以及使用指针来跟踪新分配的内存的位置。在运行阶段作决策并非OOP独有的,单使用C++编写这样的代码比使用C语言简单。

  • 使用常规变量:值是指定的量,而地址是派生量
  • 使用指针变量:地址是指定的量,而值是派生量
    int *p = 10;
    p:地址
    *p:地址所指向的量的值 (即:解除引用)
    (星号*取指针的值,&号取变量的地址)

声明和初始化指针

*左右空格可选
int* a;C++常用( int*(新的类型):即指向int的指针)
int *a;
int * a;
int* a,b 创建了一个*a ,一个b,需要都加*

  • 使用指针的金科玉律
    在指针解除引用计算*之前,将指针初始化为一个确定的、适当的地址。
int* p;
p = 0x800000;

这里左边是int*型,右边是int型,不能赋值。
需要p = (int*)x0800000

new 和 delete

指针真正的用武之地在于,在运行阶段分配未命名的内存以存储值。
C语言中,采用malloc()函数分配内存,C++使用更好地new运算符
int* p = new int;
分析:告知new运算符需要分配什么类型的内存,然后new返回一个地址再赋值给p(int类型)
但不存在p所指向量的名称
P102
(**记住:int
是一种数据类型**)

  • new所分配的内存块通常与常规变量分配的内存块不同:
    常规变量:存储在被称为栈(stack)的内存区域中
    new:存储在被称为堆(heap)的自由存储区
  • 内存耗尽
  • delete释放内存
    delete p
    不要尝试释放已经释放的内存
    避免两个指针指向同一个内存块;
    new和delete匹配使用,不能去释放通过常规量定义的指针,例如
int a = 10;
int* p = &a;
delete p;	//wrong
  • new创建动态数组和使用元素
    静态编联(stastic binding):编译时创建数组分配内存,固定内存位置和长度
    动态编联(dynamic bingding):使用new创建,如果在运行阶段需要数组,则创建,若不需要,则不创建,还可以在运行时选择长度
int *p1 = new int;
int *p2 = new int [3];
//将指针名作为数组名使用
p2[0] = 0;
p2[1] = 1;
p2[2] = 2;
p2 = p2 +1; //p2指向数组中第二个元素,则p2[0] = 1;(指向字节数加4或其他)  (数组的名字就是数组地址)P108
delete p1;
delete [] p2;	//new和delete格式需要匹配,都需要 []

P109 :指针小结

  • 指针和字符串
  • 使用new创建动态结构
  • 自动存储:在函数的周期内存在
  • 静态存储:在整个程序中都存在 static double a = 100;
  • 动态存储:new 和 delete 自由存储区
  • 栈、堆和内存泄漏 什么是堆,栈,内存泄漏和内存溢出?

数组的替代品

1.模板类vector

长度可以不固定

include<vector>
vector<type> a;
vector<type> a(n);
a.pushback(name);

vector用法

2.模板类array(C++11)

vector的功能比数组强大,但付出代价是效率较低,如果需要长度固定的数组,建议使用array,array是固定大小的,使用栈分配内存。优点是比数组更方便、更安全。

include<array>
array<type,num> a = {......};

都可以使用标准数组表示法来读取元素:例如a[2]
C++不检查数组指针超界错误

练习题

4.7

#include <iostream>
#include <string>
#include <vector>
#include <array>
using namespace std;

struct Pizza
{
	string name;
	double length;
	double weight;
};

int main()
{
	vector<int> v1;
	array<int, 10> a1;
	Pizza* p1 = new Pizza;
	cout << "please enter p1's paragrams\n";
	cout << "name: "; 
	getline(cin,p1->name);		//得到一行,包括空格
	cout << "length: "; cin >> p1->length;
	cout << "weight: "; cin >> p1->weight;
	cout << "name: " << p1->name << endl;
	cout << "length: " << p1->length << endl;
	cout << "weight: " <<  p1->weight << endl;
	delete p1;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
		a1[i] = i;
	}
	for (int j = 0; j < 10; j++)
	{
		cout << v1[j] << "and" << a1[j] << endl;
	}
	getchar();
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章