string
类字符串vector
和array
类复合类型是基于基本整型和浮点类型创建的。影响最为深远的复合类型是类。
计算机在内存中依次存储数组的各个元素。
数组的声明typeName arrayName[arraySize]
:
arraySize
不能是变量,即应该在编译时已知。
数组之所以被称为复合类型,是因为它是使用其他类型创建的。没有通用的数组类型,但存在很多特定的数组类型,如char
数组。
编译器不会检查使用的下标是否有效。但是程序运行后,超出范围的索引会引发问题,可能破坏数据或代码,也可能导致程序异常终止。
数组的初始化规则:
如果将sizeof
运算符用于数组名,得到的是整个数组的字节数;用于数组元素,得到的是元素的长度。
字符串是存储在内存的连续字节中的一系列字符。
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()
会设置失效位,并关闭后面的输入。
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.
自定义定界符时,在默认定界符之间添加任意数量的基本字符,但空格、左括号、右括号、斜杠和控制字符除外。
同一个结构可以存储多种类型的数据。
结构是用户定义的类型,而结构声明定义了这种类型的数据属性。
创建结构的步骤:
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) ;
位字段通常用在低级编程中。
能够存储不同的数据类型,但只能同时存储其中的一种类型。
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;
由于共用体每次只能存储一个值,因此它必须有足够的空间来存储最大的成员。其长度为其最大成员的长度。
提供了一种创建符号常量的方式。
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};
// 可以创建多个值相同的枚举量
指针是一个变量,它存储的是值的地址,而不是值本身。
采用地址运算符&
找到常规变量的地址。
使用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
to be continued…
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
对象可以相互赋值。