Chapter 2 Using Objects¶
1 std::string¶
字符串类
#include <string> // 头文件引入
string str; // 定义一个空字符串变量
string str = "Hello"; // 定义变量并将其内容初始化
cin >> str; // 读取键盘输入,遇到空格/回车/制表符停止读取
cout << str; // 输出到标准输出
字符串的赋值操作
char cstr1[20];
char cstr2[20] = "jaguar";
string str1;
string str2 = "panther";
cstr1 = cstr2; // illegal
str1 = str2; // legal
字符串拼接
string str3;
str3 = str1 + str2;
str1 += str2;
str1 += "lalalala"; // 支持字符串常量拼接
常用构造函数(Ctors)
// 从 C 字符串取前 len 个字符
string (const char *cp, int len);
// 从 s2 的 pos 索引开始取到末尾
string (const string& s2, int pos);
// 从 s2 的 pos 索引取 len 个字符
string (const string& s2, int pos, int len);
子字符串(Sub-string)
// 从 pos 索引提取长度为 len 的字符串,返回新的子串
substr (int pos, int len);
字符串修改
// 重新赋值字符串(支持多种重载形式)
assign(...);
// 在指定位置插入字符/字符串(通用重载版本)
insert(...);
// 在位置 pos(从 0 开始计数)处插入字符串 s
insert(int pos, const string& s);
// 删除指定位置/范围的字符
erase(...);
// 在字符串末尾添加字符/字符串
append(...);
// 从 pos 开始、长度为 len 的子串替换为字符串 s
replace(int pos, int len, const string& s);
字符串查找
// 查找子串 s 的首次出现位置,返回索引(无则返回 string::npos)
find(const string& s);
文件 IO
#include <ifstream> // 读文件
#include <ofstream> // 写文件
ifstream File2("C:\\test.txt"); // Windows 路径用双反斜杠
std::string str;
File2 >> str; // 按空格/回车分割,读取第一个单词
ofstream File1("C:\\test.txt");
File1 << "Hello World!" << endl;
io_example.cpp
#include <iostream> // 包含标准输入输出流库,用于控制台输出(cout、endl 等)
#include <fstream> // 包含文件流库,用于文件读写操作(ofstream、ifstream等)
using namespace std; // 使用标准命名空间,避免每次调用标准库时都写 std:: 前缀
int main(){
ofstream File1("c:\\1\\2.txt");
File1 << "Hello World!" << endl;
ifstream File2("C:\\1\\2.txt");
std::string str;
File2 >> str;
// 控制台输出第一次读取到的字符串内容
std::cout << "str is : " << str << std::endl;
// 继续从文件中读取下一个空白分隔的单词到 str 中
File2 >> str;
std::cout << "str is : " << str << std::endl;
return 0;
}
2 Memory Model¶
extern 关键字
- 作用:声明全局变量(表示变量在程序的某个位置定义),而非定义
- 用法:
extern int i;(仅声明,不分配内存),用于跨 .cpp 文件访问全局变量
| 变量类型 | 存储位置 | 特性 |
|---|---|---|
| Global vars | Global data | 定义在函数外,可跨 .cpp 文件共享 |
| Static global vars | Global data | 限制在当前 .cpp 文件访问,不可跨文件 |
| Static local vars | Global data | 函数内定义,值在函数调用间保持,首次访问时初始化 |
| Local vars | Stack | 函数内定义,函数执行完销毁 |
| Dynamically allocated vars | Heap | 由 new 分配,手动 delete 销毁,生命周期由程序员控制 |

示例
int i; // global vars.
static int j; // static global vars.
void f() {
int k; // local vars.
static int l; // static local vars.
int *p = malloc(sizeof(int)); // allocated vars.
}
3 Pointers to Objects¶
指针基础操作
&:取地址运算符,获取变量/对象的内存地址*:解引用运算符,通过指针访问指向的变量/对象->:指针成员访问运算符,通过对象指针调用成员函数/属性(替代(*p).)
示例
string s = "hello";
string* ps = &s; // & 取 s 的地址,ps 为 string 类型指针
cout << (*ps).size() << endl; // 解引用后访问成员,输出 5
cout << ps->size() << endl; // -> 直接访问成员,输出 5(推荐)
4 Dynamically Allocated Memory¶
动态内存分配(new/delete)
new:程序运行时分配堆内存,返回内存地址,仅能通过指针访问delete:将堆内存归还内存池,释放后指针变为野指针,建议置空(p = nullptr;)- 动态数组释放必须使用
delete[],与new[]配对
单个变量的分配与释放
int* p = new int; // 动态分配int类型变量,返回地址
int* p2 = new int(10); // 动态分配并初始化
delete p;
delete p2; // 释放堆内存
数组的分配与释放
int* parr = new int[10]; // 动态分配 int 数组,返回首元素地址
delete[] parr; // 释放数组:必须加 [],否则仅释放首元素
指针赋值与对象赋值的区别
string s1 = "a", s2 = "b";
string* ps1 = &s1, *ps2 = &s2;
s1 = s2; // 对象赋值:将 s2 的内容拷贝到 s1,二者独立
ps1 = ps2; // 指针赋值:将 ps2 的地址赋给 ps1,二者指向同一个对象
new 和 delete 的使用规范
- 不释放非
new分配的内存 - 不重复释放同一块内存
new[]必须对应delete[],new单个变量对应delete- 对空指针执行
delete是安全的
5 Reference¶
- 局部或全局变量的引用定义:
type& refname = name; - 在参数列表与成员变量中的引用:
type& refname
赋值操作将修改原对象的值
int X = 47;
int& Y = X; // Y 是 X 的引用,共用内存
cout << "Y = " << Y; // prints Y = 47
Y = 18;
cout << "X = " << X; // prints X = 18
定义时必须初始化,初始化后绑定关系不可更改
在定义时绑定
int x = 3;
int& y = x; // 引用y在定义时绑定到变量x
const int& z = x; // 常量引用z在定义时绑定到变量x
作为函数参数时绑定
void f(int& x); // 函数参数声明为引用
f(y); // 引用x在函数调用时绑定到实参y
引用的目标对象必须有实际的内存地址(不能是临时值/表达式)
void func(int &); // 函数参数为 int 类型引用
func(i * 3); // Warning or error!
| 特性 | 引用 | 指针 |
|---|---|---|
| 空值 | 不能为空 | 可为 NULL |
| 绑定 | 初始化后不可改 | 可随时切换指向 |
| 本质 | 对象别名 | 独立变量,存储地址 |
使用规则
- 无引用的引用
- 无指向引用的指针(
int&* p;非法) - 无引用数组
- 指针的引用(
void f(int*& p);)合法
6 const¶
基本定义
C++ 中的常量默认是内部链接,编译器会尽量避免为常量分配内存空间,将常量值保存在符号表中,extern 关键字会强制为常量分配内存。
const int x = 123; // 定义 const int 变量,必须初始化
x = 27; // illegal:const 变量的值不可修改
x++; // illegal
int y = x; // ok:将 const 变量的值拷贝给非 const 变量
y = x; // ok
const int z = y; // ok:非 const 可赋值给 const(更安全)
编译期常量
// 值必须在定义时初始化,除非显式使用 extern 声明
const int bufsize = 1024;
// 编译器不允许修改其值
extern const int bufsize = 1024;
// 编译期常量是编译器符号表中的条目,并非真正的变量
运行期常量(Run-time constants)
// 编译期常量可用于数组定义
const int class_size = 12;
int finalGrade[class_size]; // legal
// 运行期常量在标准 C++ 中不允许用作普通数组的长度
// 但现在部分编译器(如 GCC)支持变量长度数组(VLA) 作为扩展语法
int x;
cin >> x;
const int size = x;
double classAverage[size]; // error → ok
指针与常量(Pointers and const)
const紧邻数据类型时,修饰指向的内容;const紧邻指针变量名时,修饰指针本身
常量指针
// 指针地址固定,内容可改
char * const q = s;
*q = 'c'; // OK
q++; // ERROR
指向常量的指针
// 指向内容不可改,指针可移动
const char *p = s;
// 或:char const *p = s;
*p = 'b'; // ERROR
最严格的常量指针
// 内容与指针都不可改
const char * const r = s;
// 或:char const *const t = s;
int i; |
const int ci = 3; |
|
|---|---|---|
int * ip; |
ip = &i; |
ip = &ci; //Error |
const int *cip |
cip = &i; |
cip = &ci; |
*ip = 54; // 始终合法:ip 指向 int 类型
*cip = 54; // 永远不合法:cip 指向 const int 类型
字符串字面量(String Literals)
char* s = "Hello world!";的本质是const char*,不可修改内容,否则会导致未定义行为- 若想修改字符串,需使用数组
char s[] = "Hello world!";
类型转换(Conversions)
非常量可安全转为常量(可始终将非常量值视作常量)
void f(const int* x); // 接收指向常量整型的指针
int a = 15; // 定义非常量整型变量 a
f(&a); // OK:传入非常量变量的地址,可隐式转换为常量指针
const int b = a; // OK:用非常量 a 初始化常量 b
f(&b); // OK:常量变量的地址可传入接收常量指针的函数
常量转非常量需使用显示类型转换 const_cast
b = a + 1; // Error!
函数参数与返回值
const 值参数:函数内不可修改
// 接收常量整型参数 i
void f1(const int i) {
i++; // Illegal:编译期错误
}
const 返回值:仅限制返回值的赋值/修改行为,不影响接收方使用
int f3() { return 1; } // 返回整型值
const int f4() { return 1; } // 返回常量整型值
int main() {
const int j = f3(); // Works fine
int k = f4(); // Works fine too
}
- 大对象优先用指针/引用传递,加
const防止修改