553 C++程序设计 学习笔记

注意:本文的组织结构略有混乱

谨以此文纪念我逝去的那一个多月

数据类型和运算符

计算数组长度

int n = sizeof(arr) / sizeof(arr[0]);

枚举类型

img

运算符

* 运算符

上下文 含义 示例代码
数学运算 乘法 int result = a * b;
指针声明 定义指针 int* ptr = &x;
解引用 访问指针指向的值 int value = *ptr;

不能重载的运算符

成员访问运算符 .

成员指针访问运算符 .* ->*

域运算符 ::

长度运算符 sizeof

三元条件运算符 ?:

必须以成员函数形式重载的运算符

赋值运算符 =

下标运算符 []

函数调用运算符 ()

#include <iostream>
using namespace std;

class Calculator {
private:
int value;

public:
Calculator(int initialValue = 0) : value(initialValue) {}

// Overload function call operator to add two numbers
int operator()(int a, int b) {
return value + a + b;
}

// Another overload with three parameters
int operator()(int a, int b, int c) {
return value + a + b + c;
}
};

int main() {
Calculator calc(5);
cout << "calc(10, 15) = " << calc(10, 15) << endl; // 30
cout << "calc(10, 15, 20) = " << calc(10, 15, 20) << endl; // 50
return 0;
}

成员选择运算符 ->

存储类说明符

存储类说明符(Storage Class Specifiers) 用于定义变量或函数的存储持续时间(storage duration)、作用域(scope)以及链接性(linkage)。它们提供了对变量和函数生命周期、可见性和内存管理方式的控制。

auto

  • 含义 : auto 表示变量具有自动存储持续时间。即变量在进入其作用域时被创建,在离开作用域时被销毁。
  • 默认行为 : 在 C++11 之前,局部变量默认是 auto 类型(即使未显式声明)。从 C++11 开始,auto 被重新定义为类型推导关键字,用于自动推断变量的类型。

register

  • 含义 : 建议编译器将变量存储在寄存器中以提高访问速度。然而,现代编译器通常会忽略此建议,因为优化器已经足够智能。
  • 限制 : register 变量不能取地址(即不能使用 & 运算符)。
  • 注意 : 从 C++17 开始,register 关键字已被废弃(deprecated),不再推荐使用。

static

  • 对于局部变量static 使变量具有静态存储持续时间,即使它定义在函数内部。变量在程序启动时初始化(只初始化一次),并在程序结束时销毁
  • 对于全局变量或函数:static 限制其链接性为内部链接(internal linkage),即只能在当前文件中访问

extern

  • 含义 : extern 用于声明一个变量或函数是在其他文件中定义的,表示该变量或函数具有外部链接性(external linkage)。
  • extern 不分配内存,仅声明变量或函数的存在。
存储类说明符 存储持续时间 作用域 链接性 备注
auto 自动 局部 C++11 后主要用于类型推导
register 自动 局部 已废弃,现代编译器忽略
static 静态 局部/全局 无/内部 局部变量只初始化一次
extern 静态 全局 外部 声明外部变量或函数

语句

控制结构

顺序、选择、循环

switch

原则上每个 case 语句末尾都要写 break,若没写,则发生“贯穿”,执行所有后续语句(包括 default,直到遇到 break

int main() {
int x = 2;
switch (x) {
case 1:
cout << "1" << endl;
//break;
case 2:
cout << "2" << endl;
//break;
case -2:
cout << "-2" << endl;
//break;
default:
cout << "default" << endl;
}
return 0;
}
输出:
2
-2
default

函数

函数模板

函数模板是编写通用函数的蓝图,允许为不同类型生成逻辑相同的函数。通过模板参数(如 typename T),可以在编译时自动推导或显式指定具体类型。

// 定义模板:交换任意类型的两个值
template <typename T>
void swap(T &a, T &b) {
T temp = a;
a = b;
b = temp;
}

int main() {
int x = 1, y = 2;
swap(x, y); // 编译器生成 swap<int>(int&, int&)

double a = 3.14, b = 2.71;
swap(a, b); // 编译器生成 swap<double>(double&, double&)
}

函数重载

允许在同一作用域内定义多个同名函数,但要求它们的参数列表(参数类型、数量或顺序)不同。编译器根据调用时传入的实参类型选择最匹配的版本。

// 重载1:打印整数
void print(int value) {
std::cout << "Integer: " << value << std::endl;
}

// 重载2:打印浮点数
void print(double value) {
std::cout << "Double: " << value << std::endl;
}

// 重载3:打印字符串
void print(const char* str) {
std::cout << "String: " << str << std::endl;
}

int main() {
print(10); // 调用 print(int)
print(3.14); // 调用 print(double)
print("Hello"); // 调用 print(const char*)
}

重载模板函数

重载模板函数指在同一作用域内定义多个同名函数模板或与非模板函数共存,这些模板或函数具有不同的参数列表(如参数类型、数量或模板参数数量)。编译器根据调用时的实参类型和模板推导规则选择最匹配的版本。

编译器首选函数名和参数类型匹配的具体函数,再找模板

类和对象

浅拷贝 深拷贝

  • 浅拷贝 :直接复制对象的成员变量(包括指针)。如果成员是指针,则两个对象共享同一块内存,可能导致双重释放等问题。
  • 深拷贝 :为每个对象分配独立的内存空间,两个对象互不影响。

指针成员变量的处理

class MyClass {
private:
int* data;
public:
MyClass(int value) {
data = new int(value); // 动态分配内存
}
MyClass(const MyClass& other) {
data = new int(*other.data); // 深拷贝
}
~MyClass() {
delete data; // 释放内存
}
void setValue(int value) {
*data = value;
}
int getValue() const {
return *data;
}
};

int main() {
MyClass obj1(42);
MyClass obj2 = obj1; // 调用复制构造函数
obj1.setValue(100);
cout << "obj1.value: " << obj1.getValue() << endl;
cout << "obj2.value: " << obj2.getValue() << endl;
return 0;
}

成员函数的实现

返回值 类名 :: 函数名( 参数列表 ){ 函数体 }

构造函数

无参构造函数的实现

类名 :: 类名(){ 函数体 }

带参构造函数

定义:

类名( int 参数名1 = 5, int 参数名2 = 10);

实现:

类名 :: 类名( int 参数名1, int 参数名2){

​ 成员变量 = 参数名1;

​ 成员变量 = 参数名2;

}

带参构造函数(初始化列表)

实现:

类名 :: 类名(int 参数名1, int 参数名2) : 成员变量(参数名1), 成员变量(参数名2){ 函数体 }

析构函数

定义

~Box();

实现

Box :: ~Box(){}

复制构造函数

定义

Box(const Box& other); // const 防止被复制的对象被修改

实现

Box::Box(const Box& other){
len = other.len;
}

何时调用

  1. (新定义)用一个已存在的对象初始化一个新对象

    MyClass obj1;
    MyClass obj2 = obj1; // 调用复制构造函数
  2. (作为函数参数)将对象作为值传递给函数

    void func(MyClass obj) { }
    MyClass obj1;
    func(obj1); // 调用复制构造函数
  3. (作为函数返回值)从函数返回对象时(某些情况下):

    MyClass func() {
    MyClass obj;
    return obj; // 可能调用复制构造函数
    }

注意:对象之间的赋值 调用复制构造函数,而是调用赋值运算符

静态成员

所有该类的对象共享同一个静态成员

可以通过类名直接访问和修改静态成员

静态数据成员

存储在全局数据区(静态存储区),而不是栈或堆中

必须在类外进行定义和初始化(除非是constconstexpr类型的整型/枚举类型)。

class MyClass{
public:
static int Var;
};
int MyClass::Var = 10;

静态成员函数

静态成员函数不能访问非静态成员(包括非静态变量和非静态函数),因为它们不依赖于具体的对象实例。

可以在类中定义。

友元

允许某些函数或类访问另一个类的私有(private)和保护(protected)成员

违背了面向对象编程的封装原则

友元函数

友元函数是一个普通的全局函数或另一个类的成员函数,但它被授予了访问某个类的私有和保护成员的权限。

不能通过类的对象调用

class MyClass{
private:
int privateData;
public:
MyClass(int data) : priavteData(data){}
friend void func(MyClass &obj);
};
void func(MyClass &obj){
cout << obj.privateData;
}

友元类

一个类被授予了访问另一个类的私有和保护成员的权限

友元类的所有成员函数都可以访问被授予友元权限的类的私有和保护成员

  • 友元关系单向、不可传递、不可继承
class MyClass{
private:
int privateData;
public:
MyClass(int data) : priavteData(data){}
friend class FirendClass;
};
class FriendClass{
public:
void func(MyClass &obj){
cout<<obj.privateData;
}
}

程序结构

标识符的作用域

作用域类型 描述 示例
函数原型作用域 仅在函数声明的参数列表内有效,参数名称只在该声明中起作用,不会影响其他地方。 double getArea(double radius); // "radius" 仅在括号内有效
块作用域 在一对大括号 { } 内声明的标识符,其有效范围限定于该大括号内;局部变量即属于此范畴。 { int a = 10; // a 的作用域仅在此块内}
函数作用域 用于表示函数内部的标签(例如 goto 标签)的作用范围,这些标签在整个函数内都可见。 void func() { label: /*代码*/ goto label; // label 在整个函数内可用}
类作用域 类中声明的成员变量与成员函数的名称只在类内部有效,可通过作用域运算符访问或在类内直接引用。 class MyClass {public: int member;};MyClass obj;obj.member = 5;
命名空间/全局/文件作用域 在命名空间中(包括全局命名空间)声明的标识符,在整个命名空间或程序中均有效;对于使用 static 关键字声明的标识符,其作用域限于当前文件。 namespace NS { int n;}NS::n = 42; // n 在 NS 命名空间内有效

生命周期

#include <iostream>
using namespace std;

class A {
private:
int xx;
public:
A(int x) : xx(x) { cout << "A() " << xx << endl; }
~A() { cout << "~A() " << xx << endl; }
};

A a(1); // 全局对象

int main() {
A b(2); // 局部自动对象
static A c(3); // 静态局部对象
}

构造函数调用顺序

  1. 全局对象 a(1)
    main() 函数执行前初始化
  2. 局部对象 b(2)
    在进入 main() 后按声明顺序初始化
  3. 静态局部对象 c(3)
    在首次执行到声明位置时初始化

析构函数调用顺序

  1. 局部对象 b(2)
    main() 函数结束(作用域退出)时销毁
  2. 静态局部对象 c(3)
    在程序结束时销毁
  3. 全局对象 a(1)
    最后销毁

控制台输出结果

text
A() 1 ← 全局对象构造
A() 2 ← 局部对象构造
A() 3 ← 静态对象构造
~A() 2 ← 局部对象析构(main结束)
~A() 3 ← 静态对象析构(程序结束阶段)
~A() 1 ← 全局对象析构(最后执行)

关键机制

  1. 存储类别
    • 全局对象:静态存储期
    • static 局部对象:静态存储期
    • 普通局部对象:自动存储期
  2. 生命周期规则
    静态存储期对象按构造的逆序析构,自动存储期对象在作用域结束时立即析构。
  3. 初始化时机
    静态局部对象在首次执行到声明位置时初始化(本例中在 main() 首次执行时初始化)。

指针

动态内存分配

分配单个对象

int *p = new int; // 分配一个整型变量的内存
int *q = new int(2); // 新分配的整形变量初始化为 2

分配数组

int *arr = new int[10]; // 分配一个包含 10 个整数的数组

释放内存

delete 指针名; // 对于单个对象
delete[] 指针名; // 对于数组

继承和派生

概念

  • 继承是指一个类(称为派生类 或子类)从另一个类(称为基类 或父类)中获取成员变量和成员函数的能力。
  • 派生是指创建一个新的类(派生类),并基于已有的类(基类)进行扩展或修改的过程。

语法

定义

class 派生类名 : 访问控制 基类名 {
// 派生类的成员
};
  • public:公有继承,基类的公有和保护成员在派生类中保持原有的访问权限。
  • protected:保护继承,基类的公有和保护成员在派生类中变为保护成员。
  • private:私有继承,基类的公有和保护成员在派生类中变为私有成员。

基类的私有成员不能被派生类直接访问

  • 派生类的构造函数会自动调用基类的构造函数。

  • 如果基类没有默认构造函数,则需要在派生类的初始化列表中显式调用基类的构造函数。

多重继承

class A {};
class B {};
class C : public A, public B {}; // C 同时继承 A 和 B

菱形继承

  • 数据冗余D 中有两个独立的 A 实例。
  • 二义性 :如果访问 A 的成员,编译器无法确定应该使用哪个副本。
class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {}; // D 同时继承了两个 A 的副本

虚继承

“虚基类”是“虚继承”的结果,而“虚继承”是实现“虚基类”的手段

  • 虚基类是指在虚继承中被共享的基类实例。
class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {}; // D 中只有一个 A 的副本

多态

c++实现多态的方式:

编译时多态:函数重载、运算符重载

运行时多态:虚函数(以及函数覆写)

运算符重载

友元

需要对称性操作时,用友元

class Vector2D{
private:
double x, y;
public:
Vector2D(double x_v = 0, double y_v = 0) : x(x_v), y(y_v){}
friend Vector2D operator+(const Vector2D& lhs, const Vector2d& rhs);
friend Vector2D operator+(double scalar, const Vector2d& vec);
}
// 友元函数定义:Vector2D + Vector2D
Vector2D operator+(const Vector2D& lhs, const Vector2D& rhs) {
return Vector2D(lhs.x + rhs.x, lhs.y + rhs.y);
}
// 友元函数定义:double + Vector2D
Vector2D operator+(double scalar, const Vector2D& vec) {
return Vector2D(scalar + vec.x, scalar + vec.y);
}

需要访问类的私有或保护成员时,用友元

重载流插入运算符 << 时,由于其第一个参数 std::ostream 不属于类的一部分,因此无法通过成员函数实现

class Vector2D {
private:
double x, y;
public:
Vector2D(double x_val = 0, double y_val = 0) : x(x_val), y(y_val) {}
// 声明为友元函数
friend ostream& operator<<(ostream& os, const Vector2D& vec);
};

// 定义友元函数
ostream& operator<<(ostream& os, const Vector2D& vec) {
os << "(" << vec.x << ", " << vec.y << ")";
return os;
}

引用

返回当前对象本身

赋值运算符 = 或复合赋值运算符 +=

MyClass& operator=(const MyClass& other) {
if (this != &other) { // 防止自赋值
value = other.value;
}
return *this; // 返回当前对象的引用
}

返回流对象

流插入运算符 << 和流提取运算符 >>

class MyClass {
private:
int value;
public:
MyClass(int v = 0) : value(v) {}
friend ostream& operator<<(ostream& os, const MyClass& obj);
};

// 定义友元函数
ostream& operator<<(ostream& os, const MyClass& obj) {
os << obj.value;
return os; // 返回流对象的引用
}

返回可修改的对象

重载下标运算符 []

class IntArray {
private:
int data[10];
public:
int& operator[](int index) {
return data[index]; // 返回数组元素的引用
}

const int& operator[](int index) const {
return data[index]; // 返回常量引用
}
};

++运算符

  • 后置++的int参数是一个哑参数,只用于区分前置和后置版本
class Counter {
private:
int value;
public:
Counter(int val = 0) : value(val) {}
int getValue() const {
return value;
}
// 声明友元函数 - 前置++
friend Counter& operator++(Counter& c);
// 声明友元函数 - 后置++
friend Counter operator++(Counter& c, int);
};
Counter& operator++(Counter& c) {
++c.value;
// 返回引用以允许链式操作
return c;
}
Counter operator++(Counter& c, int) {
Counter temp = c;
++c.value;
return temp;
}

虚函数

当基类指针绑定派生类对象时,若基类和派生类都定义了一个普通的同名函数(非虚函数),会调用基类的函数。

最佳实践:如果一个基类含有虚函数,则其析构函数应声明为虚函数

若析构函数不是虚函数,则只会调用基类的虚构函数。但构造函数都会调用到。

warning: delete called on non-final ‘A’ that has virtual functions but non-virtual destructor

虚函数virtual function)是一种特殊的成员函数,它允许在派生类中重新定义基类的函数,并通过基类指针或引用调用时实现动态绑定(也称为运行时多态)。

允许基类和派生类共享相同的接口,但具体的行为由派生类决定

虚函数的动态绑定需要维护虚函数表、虚表指针

虚函数不能是静态成员函数

虚函数不能是构造函数

#include <iostream>
using namespace std;

class Animal {
public:
virtual void speak() { // 虚函数
cout << "Animal speaks" << endl;
}
virtual ~Animal() {} // 虚析构函数
};

class Dog : public Animal {
public:
void speak() override { // 重写虚函数
cout << "Dog barks" << endl;
}
};

class Cat : public Animal {
public:
void speak() override { // 重写虚函数
cout << "Cat meows" << endl;
}
};

int main() {
Animal* animal1 = new Dog(); // 基类* = new 派生类();
Animal* animal2 = new Cat();

animal1->speak(); // 输出: Dog barks
animal2->speak(); // 输出: Cat meows

delete animal1;
delete animal2;
return 0;
}

空基类指针

根据条件分配对象

class Base {
public:
virtual void show() {
cout << "Base class show()" << endl;
}
virtual ~Base() {}
};

class Derived1 : public Base {
public:
void show() override {
cout << "Derived1 class show()" << endl;
}
};

class Derived2 : public Base {
public:
void show() override {
cout << "Derived2 class show()" << endl;
}
};

int main() {
Base* ptr = nullptr; // 定义空基类指针

int choice;
cout << "Enter choice (1 or 2): ";
cin >> choice;

if (choice == 1) {
ptr = new Derived1();
} else if (choice == 2) {
ptr = new Derived2();
}

if (ptr) { // 检查指针是否为空
ptr->show();
delete ptr;
} else {
cout << "Invalid choice!" << endl;
}

return 0;
}

纯虚函数 抽象类

拥有纯虚函数的类是抽象类

纯虚函数 强制派生类实现它

抽象类不能被实例化

#include <iostream>
using namespace std;

class Shape {
public:
virtual void draw() = 0; // 纯虚函数
virtual ~Shape() {}
};

class Circle : public Shape {
public:
void draw() override {
cout << "Drawing a circle" << endl;
}
};

class Rectangle : public Shape {
public:
void draw() override {
cout << "Drawing a rectangle" << endl;
}
};

int main() {
Shape* shape1 = new Circle();
Shape* shape2 = new Rectangle();

shape1->draw(); // 输出: Drawing a circle
shape2->draw(); // 输出: Drawing a rectangle

delete shape1;
delete shape2;
return 0;
}

文件、流、字符串

标准输入输出流

  • std::cin :标准输入流,通常与键盘关联。
  • std::cout :标准输出流,通常与屏幕关联。
  • std::cerr :标准错误流,用于输出错误信息,不带缓冲。
  • std::clog :标准日志流,用于输出日志信息,带缓冲。

文件操作

C++提供了以下三种主要的文件流类,定义在头文件 <fstream> 中:

  • std::ifstream :用于从文件中读取数据(输入文件流)。
  • std::ofstream :用于向文件中写入数据(输出文件流)。
  • std::fstream :既可以读取也可以写入文件(双向文件流)。

写入文件

#include <iostream>
#include <fstream>
using namespace std;

int main() {
ofstream outFile("file.out") // 打开文件(如果不存在则创建)
if (!outFile) {
cerr << "Error opening file!" << endl;
return 1;
}

outFile << "Hello, File!" << endl; // 写入数据
outFile.close(); // 关闭文件

cout << "Data written to file." << endl;
return 0;
}

读取文件

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main() {
ifstream inFile("example.txt"); // 打开文件
if (!inFile) {
cerr << "Error opening file!" << endl;
return 1;
}

string line;
while (getline(inFile, line)) { // 逐行读取文件内容
cout << line << endl;
}

inFile.close(); // 关闭文件
return 0;
}

输入字符串流

std::istringstream 的核心功能是将字符串视为输入流,并允许使用流操作符(如 >>)从中提取数据

  • std::cin:从标准输入(如键盘)读取数据。
  • std::istringstream:从字符串中读取数据。

构造函数

std::string data = "123 456";
std::istringstream iss(data); // 将字符串 "123 456" 包装为输入流

提取数据

// 接上文
int a, b;
iss >> a >> b; // 提取两个整数
// 此时 a = 123, b = 456

例题

若文件file.txt中有以下数据: C:123,5,40;T:1,2,32,50,60,3;R:6,8,8,100 其中,C的数据表示圆的圆心坐标和半径,T的数据表示三角形的三个坐标,R的数据表示矩形的两个对角的坐标。C++如何将这些数据读取出来,放在适当的变量中。

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>

struct Circle {
int x, y; // 圆心坐标
int radius; // 半径
};

struct Triangle {
std::vector<int> points; // 存储三角形的顶点坐标
};

struct Rectangle {
int x1, y1, x2, y2; // 矩形的两个对角坐标
};

void parseData(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Error: Could not open file!" << std::endl;
return;
}

std::string line;
Circle circle;
Triangle triangle;
Rectangle rectangle;

while (std::getline(file, line)) { // 逐行读取文件
std::istringstream iss(line); // 使用字符串流解析每一行
char type;
char colon;
iss >> type >> colon; // 读取类型标识符(如 'C:')

if (type == 'C' && colon == ':') { // 解析圆的数据
char comma;
iss >> circle.x >> comma >> circle.y >> comma >> circle.radius;
std::cout << "Circle: Center(" << circle.x << ", " << circle.y
<< "), Radius = " << circle.radius << std::endl;
} else if (type == 'T' && colon == ':') { // 解析三角形的数据
int value;
char comma;
triangle.points.clear(); // 清空之前的点
while (iss >> value) {
triangle.points.push_back(value);
iss >> comma; // 跳过逗号
}
std::cout << "Triangle Points: ";
for (int point : triangle.points) {
std::cout << point << " ";
}
std::cout << std::endl;
} else if (type == 'R' && colon == ':') { // 解析矩形的数据
char comma;
iss >> rectangle.x1 >> comma >> rectangle.y1 >> comma
>> rectangle.x2 >> comma >> rectangle.y2;
std::cout << "Rectangle: Diagonal((" << rectangle.x1 << ", "
<< rectangle.y1 << "), (" << rectangle.x2 << ", "
<< rectangle.y2 << "))" << std::endl;
} else {
std::cerr << "Error: Invalid format in line: " << line << std::endl;
}
}

file.close();
}

int main() {
std::string filename = "file.txt";
parseData(filename);
return 0;
}

与字符串有关的头文件

语言 头文件 用途说明
C <string.h> 提供 C 风格字符串操作函数,如 strcpy()strcat()strlen()strcmp() 等,用于处理以 null 结尾的字符数组
C++ <string> 定义 C++ 标准库中的字符串类,如 std::stringstd::wstring,提供面向对象的字符串操作以及丰富的成员函数
C++ <cstring> C++ 对应的 C 头文件版本,将 <string.h> 中的函数放入 std 命名空间中使用

cctype

<cctype> 是 C++ 标准库中的一个头文件,它提供了许多用于字符处理的函数。这些函数主要用于检查或转换字符的属性,例如判断字符是否为字母、数字、空格等,或者将字符转换为大写或小写。

以下是 <cctype> 中一些常用函数的详细解释和示例:


常用函数列表

字符分类函数

这些函数用于检查字符的类型,返回值为布尔值(truefalse)。

函数名 功能描述 示例输入与输出
isalpha(c) 检查字符是否是字母(a-z 或 A-Z)。 isalpha('A') -> true
isdigit(c) 检查字符是否是数字(0-9)。 isdigit('5') -> true
isalnum(c) 检查字符是否是字母或数字。 isalnum('a') -> true
isspace(c) 检查字符是否是空白字符(空格、制表符、换行符等)。 isspace(' ') -> true
islower(c) 检查字符是否是小写字母(a-z)。 islower('b') -> true
isupper(c) 检查字符是否是大写字母(A-Z)。 isupper('B') -> true
ispunct(c) 检查字符是否是标点符号(非字母、数字、空白字符)。 ispunct('.') -> true

字符转换函数

这些函数用于对字符进行大小写转换。

函数名 功能描述 示例输入与输出
tolower(c) 将字符转换为小写(如果可能)。 tolower('A') -> 'a'
toupper(c) 将字符转换为大写(如果可能)。 toupper('b') -> 'B'

使用示例

以下是一个完整的代码示例,展示如何使用 <cctype> 中的函数:

#include <iostream>
#include <cctype> // 包含 cctype 头文件

using namespace std;

int main() {
char ch = 'A';

// 检查字符类型
cout << "isalpha('A'): " << isalpha(ch) << endl; // 输出 1 (true)
cout << "isdigit('A'): " << isdigit(ch) << endl; // 输出 0 (false)
cout << "isupper('A'): " << isupper(ch) << endl; // 输出 1 (true)
cout << "islower('A'): " << islower(ch) << endl; // 输出 0 (false)

// 转换字符大小写
cout << "tolower('A'): " << (char)tolower(ch) << endl; // 输出 'a'
cout << "toupper('a'): " << (char)toupper('a') << endl; // 输出 'A'

// 检查标点符号
cout << "ispunct('.'): " << ispunct('.') << endl; // 输出 1 (true)
cout << "ispunct('A'): " << ispunct('A') << endl; // 输出 0 (false)

return 0;
}

运行结果:

isalpha('A'): 1
isdigit('A'): 0
isupper('A'): 1
islower('A'): 0
tolower('A'): a
toupper('a'): A
ispunct('.'): 1
ispunct('A'): 0

substr() 的基本语法

std::string substr(size_t pos = 0, size_t len = npos) const;

  • pos : 子字符串的起始位置(索引从 0 开始)。
  • len : 要提取的字符数。
  • 返回值 : 返回一个新的 std::string 对象,表示从原字符串中提取的子字符串。

注意:

  • 如果 pos 超出了字符串的长度,则会抛出 std::out_of_range 异常。
  • 如果 len 超出了字符串剩余部分的长度,则会提取到字符串末尾。

find() 的基本语法

size_t find(const string& str, size_t pos = 0) const;
size_t find(const char* s, size_t pos = 0) const;
size_t find(char ch, size_t pos = 0) const;

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

int main() {
string str = "Hello, World!";

// 查找子字符串 "World"
size_t pos = str.find("World");

if (pos != string::npos) {
cout << "Substring 'World' found at position: " << pos << endl;
} else {
cout << "Substring not found." << endl;
}

return 0;
}
  • 参数说明 :
    • strs: 要查找的子字符串(可以是 std::string 或 C 风格字符串)。
    • ch: 要查找的单个字符。
    • pos: 查找的起始位置,默认为 0(从字符串开头开始查找)。
  • 返回值 :
    • 如果找到目标子字符串或字符,返回其首次出现的位置索引 (从 0 开始)。
    • 如果未找到,返回 std::string::npos(一个特殊常量,表示“未找到”)。

其他

随机数

#include <iostream>
#include <cstdlib>
#include <ctime>

int main() {
// 初始化随机数种子
srand((unsigned)time(0));
int min = 10, max = 20;
// 生成 [min, max] 范围内的随机数
int random_number = min + rand() % (max - min + 1);

std::cout << "Random Number in range [" << min << ", " << max << "]: " << random_number << std::endl;
return 0;
}

接口和实现方法相分离的优点

模块换、低耦合:每个模块只依赖于定义良好的接口而不需要关心具体实现细节,从而降低了模块间的耦合度

易维护、高拓展:开发者可以在不改变接口的前提下修改或替换实现部分。

提高代码可重用性、可测试性