C++ Primer Plus学习笔记04-复合类型

  • 数组、C-风格字符串和string类字符串
  • 结构、共用体、枚举和指针
  • 管理动态内存
  • 动态数组和动态结构
  • 自动存储、静态存储和动态存储
  • vectorarray

复合类型是基于基本整型和浮点类型创建的。影响最为深远的复合类型是类。


1 数组(array)

计算机在内存中依次存储数组的各个元素。

数组的声明typeName arrayName[arraySize]

  • 存储在每个元素中的值的类型
  • 数组名
  • 数组中的元素数

arraySize不能是变量,即应该在编译时已知。

数组之所以被称为复合类型,是因为它是使用其他类型创建的。没有通用的数组类型,但存在很多特定的数组类型,如char数组。

编译器不会检查使用的下标是否有效。但是程序运行后,超出范围的索引会引发问题,可能破坏数据或代码,也可能导致程序异常终止。

数组的初始化规则:

  • C++允许在声明语句中初始化数组元素,只需提供一个用逗号分隔的值列表,用花括号括起即可。
  • 只有在定义数组时才能够使用初始化。
  • 不能将一个数组赋给另一个数组
  • 可以使用下标给数组的元素赋值
  • 初始化数组时,提供的值可以少于数组的元素数目(部分初始化,剩余元素设置为0)
  • 如果初始化数组时方括号内为空,则C++编译器将计算元素个数。
  • C++11初始化数组时可以省略等号,可以不再大括号内包含任何东西(设置为0),禁止缩窄转换。

如果将sizeof运算符用于数组名,得到的是整个数组的字节数;用于数组元素,得到的是元素的长度。


2 字符串

字符串是存储在内存的连续字节中的一系列字符。

C++处理字符串的方式有两种:C-风格字符串、基于striing类库的方法

C-风格字符串:以空字符(null character)\0结尾。

char dog[8] = {'b', 'e', 'a', 'u', 'x', ' ', 'I', 'I'}; // not a string
char cat[8] = {'f', 'a', 't', 'e', 's', 's', 'a', '\0'}; // a string
char bird[11] = "Mr. Cheeps"; 
char fish[] = "Bubble";

cout处理字符串时,逐个处理其中的字符直到达到空字符为止。
cin使用空白来确定字符串的结束位置。

字符串常量实际上表示的是字符串所在的内存地址。

字符串常量拼接

任何两个由空白分隔的字符串常量都将自动拼接称一个,拼接时不会在被连接的字符串之间添加空格。

// storing strings in an array
#include 
#include  // for the strlen() function
int main(){
  using namespace std;
  const int Size = 15;
  char name1[Size]; // empty array
  char name2[Size] = "C++owboy"; // initialized array
  // NOTE: some implementations may require the static keyword
  // to initialize the array name2
  
  cout << "Howdy! I'm " << name2;
  cout << "! What's your name?\n";
  cin >> name1;
  cout << "Well, " << name1 << ", your name has ";
  cout << strlen(name1) << " letters and is stored\n";
  cout << "in an array of " << sizeof(name1) << " bytes.\n";
  cout << "Your initial is " name1[0] << ".\n";
  name2[3] = '\0';
  cout << "Here are the first 3 character of my name: ";
  cout << name2 << endl;
  return 0;
}

每次读取一行字符串输入

面向行的输入cin.getline(arrayName, numChars),使用换行符确定输入结尾,numChars包含空字符。
面向行的输入cin.get(arrayName, numChars):不再读取并丢弃换行符,而是将换行符保留在输入队列中;如果紧接着第二次调用该函数,看到的第一个字符就是换行符。使用cin.get()可以读取下一个字符。

cin.get(name, ArSize); // first line
cin.get();	// new line
cin.get(dessert, ArSize); // second line

// or
cin.get(name, ArSize).get();
// cin.get(name, ArSize)返回一个cin对象 

get()读取空行后将设置失效位,即接下来的输入将被阻断,可以通过命令cin.clear();恢复输入。

如果输入字符串比分配空间长,则getline()get()将把余下的字符留在输入队列中,而getline()会设置失效位,并关闭后面的输入。

3 string

string类使用起来比数组简单,同时提供了将字符串作为一种数据类型的表示方法。

头文件string

// using the C++ string class
#include 
#include  // make string class available
int main(){
  using namespace std;
  char charr1[20];
  char charr2[20] = "jaguar";
  string str1;
  string str2 = "panther";
  string first_date = {"The Bread Bowl"};

  cout << "Enter a kind of feline: ";
  cin >> charr1;
  cout << "Enter another kind of feline: ";
  cin >> str1;
  cout << "Here are soome felines:\n"
  cout << charr1 << " " << charr2 << " "
       << str1 << " " << str2 
       << endl;
  cout << "The third letter in " << charr2 << " is " << charr2[2] << endl;
  cout << "The third letter in " << str2 << " is " << str2[2] << endl;
  return 0;
}

string类简化了字符串合并操作,可以使用运算符++=

// assigning, adding, and appending
#include 
#include 
int main(){
  using namespace std;
  string s1 = "penguin";
  string s2, s3;

  // You can assign one string object to another
  s2 = s1;
  // You can assign a C-style string to a string object
  s2 = "buzzard";
  // You can concatenate strings
  s3 = s1 + s2;
  // You can append strings
  s1 += s2;
  s2 += " for a day";
  return 0;
}
// more string class features
#include 
#include 
#include  // C-style string library
int main(){
  using namespace std;
  char charr1[20];
  char charr2[20] = "jaguar";
  string str1;
  string str2 = "panther";

  // assignment for string objects and character arrays
  str1 = str2;
  strcpy(charr1, charr2);
  // appending for string objects and character arrays
  str1 += " paste";
  strcat(charr1, " joice");
  // finding the length of a string obejct and a C-style string
  int len1 = str1.size();
  int len2 = strlen(charr1);
  return 0;
}

string类具有自动调整大小的功能。

string类I/O

// line input 
#include 
#include 
#include 
int main(){
  using namespace std;
  char charr[20];
  string str;

  cout << "Length of string in charr before input: "
       << strlen(charr) << endl;
       // 对于未初始化的数组而言,内容是未定义的
  cout << "Length of string in str before input: "
       << str.size() << endl;
       // 未初始化的string对象长度为0
  cout << "Enter a line of text: \n";
  cin.getline(charr, 20);
  cout << "You entered: " << charr << endl;
  cout << "Enter another line of text:\n";
  getline(cin, str);  // cin now an argument, no length specifier
  cout << "You entered: " << str << endl;
  cout << "Length of string in charr after input: " 
       << strlen(charr) << endl;
  cout << "Length of string in str after input: " 
       << str.size() << endl;
  return 0;
}

C++新增原始(raw)字符串。原始字符串中,字符表示的就是自己,即没有转移字符等。原始字符串将"()"作为定界符,以前缀R标识。

cout << R"(Jim "King" Tutt uses "\n" instead of endl.)" << endl;

原始字符串语法允许在表示字符串开头的"(之间添加其他字符,这意味着标识字符串结尾的")之间也必须包含这些字符。

cout << R"+*("(Who wouldn't?)", she whispered.)+*" << endl;
// "(Who wouldn't?)", she whispered.

自定义定界符时,在默认定界符之间添加任意数量的基本字符,但空格、左括号、右括号、斜杠和控制字符除外。

4 结构

同一个结构可以存储多种类型的数据。
结构是用户定义的类型,而结构声明定义了这种类型的数据属性。

创建结构的步骤:

  1. 定义结构描述:描述并标记能够存储在结构中的各种数据类型
  2. 按描述构建结构变量
struct newTypeName
{
  // struct member list
  typeName1 name1;
  typeName2 name2;
  // ...
};

newTypeName object1;
object1.name1; // 用成员运算符(.)访问成员

结构声明的位置很重要:放在main()函数中,紧跟在开始括号的后面——内部声明,只能被该声明所属的函数使用;或者放在main()函数前面——外部声明,可以被其后的任何函数使用。

// a simple structure
#include 
struct inflatable // structure declaration
{
  char name[20];
  float volumn;
  double price;
};

int main()
{
  using namespace std;
  inflatable guest =  // 花括号初始化
  {
    "Glorious Gloria", 	// name value
    1.88,				// volumn value
    29.99				// prive value
  }; // guest is a structure variable of type inflatable
  // Note: some implementations require using
  // static inflatable guest = 

  cout << "Expanding your guest list with " << guest.name << "!\n";
  cout << "You can have it for $" << guest.price << "!\n";
  return 0;
}

如果结构初始化的大括号内未包含任何东西,则各个成员被设置为零。

结构可以使用赋值运算符=——成员赋值(memberwise assignment)

C++结构除了成员变量之外,还可以有成员函数。

结构数组

结构中的位字段

C++也允许指定占用特定位数的结构成员,这使得创建与某个硬件设备上的寄存器对应的数据结构非常方便。

struct torgle_register
{
  unsigned int SN : 4; // 4 bits for SN value
  unsigned int : 4; // 4 bits unused
  bool goodIn : 1; // valid input (1 bit)
  bool goodTorgle : 1; // successful torgling
};

torgle_register tr = {14, true, false};
if(tr.goodIn) ;

位字段通常用在低级编程中。

5 共用体(union)

能够存储不同的数据类型,但只能同时存储其中的一种类型。

union one4all
{
  int int_val;
  long long_val;
  double double_val;
};

one4all pail;
pail.int_val = 15;
cout << pail.int_val;
pail.double_val = 1.38; // store a double, int value is lost
cout << pail.double_val;

由于共用体每次只能存储一个值,因此它必须有足够的空间来存储最大的成员。其长度为其最大成员的长度。

6 枚举(enum)

提供了一种创建符号常量的方式。

enum spectrum {red, orange, yellow, green, blue, violet, indigo, ultraviolet};

在默认情况下,将整数值赋给枚举量,第一个枚举量的值为0,第二个为1,以此类推。可以显式地指定整数值覆盖默认值。

spectrum band;
band = blue; // valid
band = 2000; // invalid, 2000 not an enumerator
++band; // not valid
band = orange + red; // not valid 

枚举量是整型,可以被提升为int类型,但int类型不能自动转换为枚举类型。

实际上,枚举更常被用来定义相关的符号变量,而不是新类型。

设置枚举量的值

enum bits{one=1, two=2, four=4, eight=8};
enum bigstep{first, second=100, third};
// 默认情况下first=0,后面没有被初始化的枚举量值比前一个大1
enum {zero, null=0, one, numero_uno=1};
// 可以创建多个值相同的枚举量

7 指针和自由存储空间

指针是一个变量,它存储的是值的地址,而不是值本身。

采用地址运算符&找到常规变量的地址。

使用OOP时,可能在运行阶段确定数组的长度。C++采用的方法是,使用关键字new请求正确数量的内存以及使用指针来跟踪新分配的内存位置。

指针名表示的是地址。*运算符被称为间接值(indirect value)或解除引用(dereferencing)运算符,将其用于指针,可以得到该地址处存储的值。

声明和初始化指针

int * p_updates;
// 对每个指针变量名,都需要使用一个*
// 指针都是基于类型的

// 可以在声明中初始化指针
int higgens = 5;
int * pt = &higgens;

指针的危险

在C++中创建指针时,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向数据的内存——为数据提供空间是一个独立的步骤。

long * fellow;
*fellow = 223323; // place a value in a never-never land
// 一定要在对指针应用*之前,将指针初始化为一确定的、适当的地址

要将数字值作为地址使用,应该通过强制类型转换。

int * pt;
pt = (int *) 0xB8000000;

new分配内存&用delete释放内存

指针真正的用武之地在于,在运行阶段分配未命名的内存以存储值。

在C语言中可以使用库函数malloc()分配内存;C++仍可以这样做,但是有一个更好的方法——new运算符

delete运算符在使用完内存后,将其还给内存池。对空指针使用delete是安全的。

int * pn = new int;

delete p; // 释放p指向的内存

动态联编(dynamic binding),运行阶段需要数组时创建数组。

int * psome = new int [10]; // now psome point to the first element
psome = psome + 1; // now psome point to the second element;
psome = psome - 1;
delete [] psome; // free a dynamic array

8 指针、数组和指针算术

to be continued…

9 类型组合

10 数组的替代品

模板类vector

是一种动态数组,可以在运行阶段设置它的长度,可插入数据。使用堆存储。

#include 
// ...
using namespace std;
vector<int> vi; // create a zero-size array of int
int n;
cin >> n;
vector<double> vd(n); // create an array of n doubles

vector的效率稍低

模板类array(C++11)

长度固定,使用栈(静态内存分配),因此效率与数组相同但更方便、安全。

#include 

//...
using namespace std;
array<int, 5> ai; // create array object of 5 ints
array<double, 4> ad;

ai.at(1) = 2 // assign 2 to ai[1]

array对象可以相互赋值。

你可能感兴趣的:(#C++,c++,指针)