Skip to content

C++基础

约 6845 字大约 23 分钟

2025-09-21

存储

1字节(byte)存8位(bit),每位放0-1,int可以放4字节,就是32位,可以存23212^{32}-1. float 4 double8 longlong8 sizeof操作符可以获取占用字节

杂项

  1. endl\n效果等价
  2. C语言的变量声明必须在前面
  3. C++不需要加关键字enum,struct.
  4. main函数外面定义的变量是全局变量,在局部变量中使用全局变量(在同名时常用)在变量名前加::.
  5. 将数组传入函数中会退化为指针

指针

在C++遵循从右向左的变量声明

const char* name = "Hello World"; // const 修饰 char 无法修改部分  但是指针name本身可以修改

    char* const name = "Hello World"; // 指针本身总体指向的不能修改,但是它其中的部分可以修改

    const char* const name = "Hello World"; // 都不能修改

![[Pasted image 20250228104305.png]]

内联函数

作用:减少开销,提高速度 inline int func()

  1. 需要在调用前声明
  2. 不能含有复杂的语句,如for switch

联合

联合类型名 联合变量名
union
    {
        int i = 5;
        double d;
    } x;
    cout << x.i;

内存分配和释放(new delete

指针变量 = new 类型(初值)
#include <iostream>
using namespace std;
int main(){
    int * ptr;
    ptr = new int; // 分配存储空间
    *ptr = 10; // 前面的*解引用,获取其中本来的值
    cout << *ptr; // 同上
    delete ptr;

}

引用

作用:给变量起另一个名字 类型 & 新引用名 = 已定义的一个变量名

int i = 5;
int &j = i;

注意:

  1. 声明时一定要立刻初始化
  2. 初始化后不能更改为其他引用
  3. 不能建立void类型引用
  4. 不能建立引用的引用 int &&r = n
  5. 地址操作符只是碰巧符号相同

指针通过地址间接访问变量,引用通过别名访问

指针和引用

指针变量作为函数参数

#include <iostream>
using namespace std;
void swap(int *a,int *b){ // 参数名为指针
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
int main(){
    int a=5,b=10;
    cout << a << b << endl;
    swap(&a,&b); // 取指针
    cout << a << b;

}

引用作为函数参数

#include <iostream>
using namespace std;
void swap(int &a, int &b) // 作为引用变量,直接操作引用原来的变量更改
{
    int temp;
    temp = a;
    a = b;
    b = temp;
}
int main()
{
    int a = 5, b = 10;
    cout << a << ',' << b << endl;
    swap(a, b);
    cout << a << ',' << b << endl;
}

使用引用返回函数值

#include <iostream>
using namespace std;
int a[] = {1, 3, 5, 7, 9};
int &index(int); // 声明函数,并要返回一个变量的引用
int main()
{
    index(2) = 25;
    cout << a[2];
}
int &index(int i)
{
    return ::a[i]; // 相当于直接返回a[i]的地址,到时候如果直接给另一个变量,另一个变量的修改也能修改到这个元素
}

引用和指针的区别 在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。但其实在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。

引用概念上定义一个变量的别名,指针存储一个变量地址。 引用在定义时必须初始化,指针没有要求 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体 在sizeof中含义不同 :引用结果为引用类型的大小,但指针始终是地址空间,所占字节个数为32/64个字节 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小 有多级指针,但是没有多级引用 访问实体方式不同,指针需要显式解引用,引用编译器自己处理

类和对象

例子:求复数的绝对值

结构体写法

#include <iostream>
#include <cmath>
using namespace std;
int main()
{
    struct Complex
    {
        double real;
        double imag;
        void init(double r, double i)
        {
            real = r;
            imag = i;
        }
        double absComplex()
        {
            return sqrt(real * real + imag * imag);
        }
    };

    Complex a;
    a.init(3, 4);
    cout << "绝对值" << a.absComplex();
}

在C++中,. 和 -> 运算符用于访问对象的成员,但它们的使用场景有所不同:

  1. 点运算符 (.):

    • 用于访问一个对象的成员。
    • 语法为:对象.成员
    • 例如,如果你有一个 Student 类的对象 s,你可以使用 s.Print(); 来调用 s 对象的 Print 成员函数。
  2. 箭头运算符 (->):

    • 用于访问一个指针所指向的对象的成员。
    • 语法为:指针->成员
    • 例如,如果你有一个指向 Student 类对象的指针 p,你可以使用 p->Print(); 来调用 p 指向的对象的 Print 成员函数。

简而言之,. 用于直接访问对

class写法

class Complex
    {
    [private:]
        double real;
        double imag;

    public:
        void init(double r, double i)
        {
            real = r;
            imag = i;
        }
        double absComplex()
        {
            return sqrt(real * real + imag * imag);
        }
    };

    Complex a;
    a.init(3, 4);
    cout << "绝对值" << a.absComplex();

我们还可以设置protected设置半隐蔽的成员变量.

我们可以在类外面定义成员函数 同时它可以访问私有成员. 注意写在main函数外部

#include <iostream>
#include <cmath>
using namespace std;
class Complex
{

    double real;
    double imag;

public:
    void init(double r, double i)
    {
        real = r;
        imag = i;
    }
    double absComplex()
    {
        return sqrt(real * real + imag * imag);
    }
    void set(double r, double i);
};
void Complex::set(double r, double i)
{
    real = r;
    imag = i;
}
int main()
{

    Complex a;
    a.init(3, 4);
    cout << "绝对值" << a.absComplex();
}

如果在::前面不写任何类,就默认让他是普通函数. 同时在类外面定义的时候需要说明参数表中参数的类型和参数名. 也可以写内联函数,你可以在内部声明或者外部定义的时候任意一处写inline即可 class声明完毕后要加分号

示例:日期

#include <iostream>
using namespace std;
class Date
{
    int year, month, day;

public:
    void init(int y, int m, int d)
    {
        year = y;
        month = m;
        day = d;
    }
    void toNextDay()
    {
        day++;
    }
    void getDay()
    {
        cout << year << '.' << month << '.' << day;
    }
};

int main()
{
    Date today;
    today.init(2025, 2, 28);
    today.toNextDay();
    today.getDay();
    return 0;
}

同时我们可以使用指针指向。(但要求指向的是共有成员变量)

Date d,*ptr;
ptr = &d;

可以这样访问: d.year (*ptr).year ptr->year都可以访问,等价

output = {
	"result":true/false,
	"reason":"xxxxx"
}

structclass区别

区别

  • struct的默认访问权限是public,而class的默认访问权限是private。
  • struct的默认继承方式是public,而class的默认继承方式是private。
  • struct的默认成员类型是public,而class的默认成员类型是private。
  • 在使用struct时可以省略关键字struct,而在使用class时不能省略关键字class。
  • 在C++中,struct和class可以互相继承,区别仅在于默认访问权限和默认继承方式的不同。

构造函数

构造函数是一种特殊的成员函数,它在创建对象时自动调用。它的名字与类名相同,没有返回类型。构造函数用于初始化对象的成员变量。

默认构造函数 如果一个类没有定义任何构造函数,编译器会自动生成一个默认构造函数。这个默认构造函数不接受任何参数,并且不对成员变量进行初始化。

带参数的构造函数 你可以定义带参数的构造函数,以便在创建对象时为成员变量提供初始值。

#include <iostream>
using namespace std;

class Box {
public:
    // 默认构造函数
    Box() {
        cout << "调用默认构造函数" << endl;
        length = 0;
        width = 0;
        height = 0;
    }

    // 带参数的构造函数
    Box(int l, int w, int h) {
        cout << "调用带参数的构造函数" << endl;
        length = l;
        width = w;
        height = h;
    }

    int length;
    int width;
    int height;
};

int main() {
    // 使用默认构造函数创建对象
    Box box1;
    cout << "box1 的尺寸: " << box1.length << ", " << box1.width << ", " << box1.height << endl;

    // 使用带参数的构造函数创建对象
    Box box2(10, 20, 30);
    cout << "box2 的尺寸: " << box2.length << ", " << box2.width << ", " << box2.height << endl;

    return 0;
}

在这个例子中,Box 类有两个构造函数:一个默认构造函数和一个接受三个整数参数的构造函数。当你创建 box1 对象时,调用的是默认构造函数。当你创建 box2 对象时,调用的是带参数的构造函数。

析构函数

析构函数是与构造函数相对应的特殊成员函数。它在对象被销毁时自动调用,用于释放对象所占用的资源,例如动态分配的内存。析构函数的名字是在类名前面加上波浪号(~),并且它不接受任何参数,也没有返回类型。

默认析构函数 如果一个类没有定义析构函数,并且它没有使用动态内存分配,那么编译器会自动生成一个默认析构函数。这个默认析构函数不做任何事情。

自定义析构函数 如果你在类中使用了动态内存分配(例如使用 new),那么你需要定义一个析构函数来释放这些内存,以防止内存泄漏。

#include <iostream>
using namespace std;

class MyClass {
public:
    int* data;

    // 构造函数,分配内存
    MyClass(int size) {
        cout << "分配内存..." << endl;
        data = new int[size];
    }

    // 析构函数,释放内存
    ~MyClass() {
        cout << "释放内存..." << endl;
        delete[] data;
    }
};

int main() {
    // 创建对象,调用构造函数
    MyClass obj(10);

    // 对象在 main 函数结束时被销毁,自动调用析构函数
    return 0;
}

在这个例子中,MyClass 的构造函数使用 new 分配了一个整数数组。析构函数使用 delete[] 来释放这块内存。当 obj 对象离开作用域(在 main 函数结束时)时,析构函数 ~MyClass() 会被自动调用,从而防止内存泄漏。

拷贝构造函数

拷贝构造函数是一种特殊的构造函数,它在创建对象时,使用一个已存在的同类对象来初始化新对象。它通常用于按值传递对象给函数,或者从函数按值返回对象。

拷贝构造函数的第一个参数必须是类类型对象的引用(通常是 const 引用),其他参数要有默认值。

#include <iostream>
using namespace std;

class Point {
public:
    int x, y;

    // 构造函数
    Point(int x_val = 0, int y_val = 0) : x(x_val), y(y_val) {
        cout << "调用构造函数" << endl;
    }

    // 拷贝构造函数
    Point(const Point& other) : x(other.x), y(other.y) {
        cout << "调用拷贝构造函数" << endl;
    }

    void display() {
        cout << "(" << x << ", " << y << ")" << endl;
    }
};

// 按值传递对象,会调用拷贝构造函数
void printPoint(Point p) {
    cout << "在 printPoint 函数中: ";
    p.display();
}

int main() {
    Point p1(1, 2); // 调用构造函数

    Point p2 = p1; // 调用拷贝构造函数,初始化 p2
    cout << "p2: ";
    p2.display();

    Point p3(p1); // 调用拷贝构造函数,初始化 p3
    cout << "p3: ";
    p3.display();

    printPoint(p1); // 按值传递 p1,调用拷贝构造函数

    return 0;
}

在这个例子中,Point 类有一个拷贝构造函数 Point(const Point& other)

  • Point p2 = p1;Point p3(p1); 都调用了拷贝构造函数来创建 p2p3
  • printPoint(p1); 函数按值传递 p1,这会创建一个 p1 的副本,因此也调用了拷贝构造函数。

拷贝赋值运算符

拷贝赋值运算符允许你将一个已存在的对象的值赋给另一个已存在的同类对象。它与拷贝构造函数类似,但用于赋值操作而不是初始化。

拷贝赋值运算符通常声明为 类名& operator=(const 类名& other)。它返回一个对当前对象的引用,以便支持链式赋值。

#include <iostream>
using namespace std;

class Rectangle {
public:
    int width, height;

    // 构造函数
    Rectangle(int w = 0, int h = 0) : width(w), height(h) {
        cout << "调用构造函数" << endl;
    }

    // 拷贝赋值运算符
    Rectangle& operator=(const Rectangle& other) {
        cout << "调用拷贝赋值运算符" << endl;
        if (this == &other) { // 防止自赋值
            return *this;
        }
        width = other.width;
        height = other.height;
        return *this; // 返回对当前对象的引用
    }

    void display() {
        cout << "宽度: " << width << ", 高度: " << height << endl;
    }
};

int main() {
    Rectangle rect1(10, 5);
    Rectangle rect2;

    rect2 = rect1; // 调用拷贝赋值运算符

    cout << "rect1: ";
    rect1.display();
    cout << "rect2: ";
    rect2.display();

    Rectangle rect3(2, 3);
    rect3 = rect1 = rect2; // 链式赋值
    cout << "rect3: ";
    rect3.display();

    return 0;
}

在这个例子中,Rectangle 类重载了赋值运算符 operator=

  • rect2 = rect1; 调用了拷贝赋值运算符,将 rect1 的值赋给 rect2
  • rect3 = rect1 = rect2; 是链式赋值,它从右向左执行。首先 rect1 = rect2 调用拷贝赋值运算符,然后 rect3 = rect1(此时 rect1 已经等于 rect2 的值)再次调用拷贝赋值运算符。

注意自赋值:在拷贝赋值运算符中,检查 if (this == &other) 是非常重要的,以防止对象将自身赋值给自己,这可能会导致意外的行为,尤其是在涉及动态内存时。

this 指针

this 指针是 C++ 中的一个特殊指针,它指向当前对象。在类的成员函数内部,你可以使用 this 指针来访问对象的成员变量和成员函数。当存在局部变量与成员变量同名时,this 指针就显得尤为重要。

#include <iostream>
using namespace std;

class MyClass {
public:
    int value;

    // 构造函数
    MyClass(int value) {
        // 使用 this 指针区分成员变量和参数
        this->value = value;
        cout << "对象创建,值为: " << this->value << endl;
    }

    void printValue() {
        cout << "当前对象的值是: " << this->value << endl;
    }

    // 返回指向当前对象的引用
    MyClass& increment() {
        this->value++;
        return *this; // 返回解引用的 this 指针,即当前对象本身
    }
};

int main() {
    MyClass obj1(10);
    obj1.printValue();

    MyClass obj2(20);
    obj2.printValue();

    // 使用 this 指针进行链式调用
    obj1.increment().increment();
    obj1.printValue();

    return 0;
}

在这个例子中:

  • 在构造函数 MyClass(int value) 中,this->value = value; 使用 this 指针来明确指定要赋值的是对象的成员变量 value,而不是同名的参数 value
  • printValue() 函数也使用了 this->value 来访问对象的 value 成员。
  • increment() 函数返回 *this,即当前对象本身的一个引用。这使得我们可以进行链式调用,例如 obj1.increment().increment();

继承

继承是面向对象编程的一个重要特性,它允许一个类(子类或派生类)继承另一个类(父类或基类)的属性和行为。这有助于代码重用和建立类之间的层次关系。

基本语法

class DerivedClass : access-specifier BaseClass {
    // DerivedClass 的成员
};
  • DerivedClass 是派生类。
  • BaseClass 是基类。
  • access-specifier 指定了派生类如何访问基类的成员,可以是 publicprotectedprivate

访问权限

  • public 继承: 基类的 public 成员在派生类中仍然是 publicprotected 成员仍然是 protectedprivate 成员不能被派生类访问。
  • protected 继承: 基类的 publicprotected 成员在派生类中都变成 protectedprivate 成员不能被派生类访问。
  • private 继承: 基类的 publicprotected 成员在派生类中都变成 privateprivate 成员不能被派生类访问。

示例:单继承

#include <iostream>
using namespace std;

// 基类
class Vehicle {
public:
    string brand;
    void honk() {
        cout << "嘟嘟!" << endl;
    }
};

// 派生类,public 继承
class Car : public Vehicle {
public:
    string model;
    void drive() {
        cout << "汽车在行驶..." << endl;
    }
};

int main() {
    Car myCar;
    myCar.brand = "丰田"; // 继承自 Vehicle
    myCar.model = "凯美瑞";

    myCar.honk(); // 调用继承的成员函数
    myCar.drive(); // 调用派生类自己的成员函数

    cout << "品牌: " << myCar.brand << ", 型号: " << myCar.model << endl;

    return 0;
}

在这个例子中,Car 类继承自 Vehicle 类。Car 对象可以使用 Vehicle 的成员(如 brandhonk())。

多重继承

多重继承允许一个派生类继承自多个基类。

示例:多重继承

#include <iostream>
using namespace std;

class Engine {
public:
    void startEngine() {
        cout << "引擎启动..." << endl;
    }
};

class Wheels {
public:
    void rotateWheels() {
        cout << "车轮转动..." << endl;
    }
};

// Car 类继承自 Engine 和 Wheels
class Car : public Engine, public Wheels {
public:
    void drive() {
        startEngine();
        rotateWheels();
        cout << "汽车在行驶..." << endl;
    }
};

int main() {
    Car myCar;
    myCar.drive();
    return 0;
}

在这个例子中,Car 类同时继承了 EngineWheels 的功能。

虚函数和多态

虚函数 虚函数是声明在基类中,并且在派生类中可以被重新定义的函数。它允许你通过基类指针或引用来调用派生类中重写的函数,从而实现多态。在基类中声明虚函数时,使用 virtual 关键字。

多态 多态(Polymorphism)意味着“多种形态”。在 C++ 中,多态允许你使用一个基类指针指向不同派生类的对象,并在运行时根据对象的实际类型来调用相应的函数。这通常通过虚函数和抽象类来实现。

示例:虚函数和多态

#include <iostream>
#include <vector>
#include <memory> // for std::unique_ptr

using namespace std;

// 基类
class Shape {
public:
    // 虚函数,用于计算面积
    virtual double area() const {
        cout << "Shape::area() 被调用" << endl;
        return 0.0; // 默认面积为 0
    }

    // 虚析构函数,确保派生类析构函数被正确调用
    virtual ~Shape() {
        cout << "Shape 析构函数被调用" << endl;
    }
};

// 派生类:圆形
class Circle : public Shape {
private:
    double radius;

public:
    Circle(double r) : radius(r) {
        cout << "Circle 构造函数被调用" << endl;
    }

    // 重写虚函数 area()
    double area() const override {
        cout << "Circle::area() 被调用" << endl;
        return 3.14159 * radius * radius;
    }

    ~Circle() override {
        cout << "Circle 析构函数被调用" << endl;
    }
};

// 派生类:矩形
class Rectangle : public Shape {
private:
    double width;
    double height;

public:
    Rectangle(double w, double h) : width(w), height(h) {
        cout << "Rectangle 构造函数被调用" << endl;
    }

    // 重写虚函数 area()
    double area() const override {
        cout << "Rectangle::area() 被调用" << endl;
        return width * height;
    }

    ~Rectangle() override {
        cout << "Rectangle 析构函数被调用" << endl;
    }
};

int main() {
    // 使用智能指针管理对象,避免手动内存管理
    vector<unique_ptr<Shape>> shapes;

    // 创建不同类型的 Shape 对象,并存入 vector
    shapes.push_back(make_unique<Circle>(5.0));
    shapes.push_back(make_unique<Rectangle>(4.0, 6.0));
    shapes.push_back(make_unique<Circle>(2.5));

    // 遍历 vector,调用每个对象的 area() 函数
    // 由于 area() 是虚函数,运行时会根据对象的实际类型调用相应的派生类函数
    for (const auto& shape_ptr : shapes) {
        cout << "计算面积: " << shape_ptr->area() << endl;
        cout << "--------------------" << endl;
    }

    // 当 vector 超出作用域时,智能指针会自动销毁对象,并调用正确的析构函数
    return 0;
}

代码解释

  1. Shape 基类:

    • virtual double area() const: 声明了一个虚函数 area()const 表示这个函数不会修改对象的状态。
    • virtual ~Shape(): 声明了一个虚析构函数。这是非常重要的!当通过基类指针删除派生类对象时,虚析构函数确保派生类的析构函数会被正确调用,从而防止内存泄漏。
  2. CircleRectangle 派生类:

    • 它们都继承自 Shape
    • double area() const override: 使用 override 关键字明确表示这个函数重写了基类的虚函数。这有助于编译器检查重写是否正确。
    • 它们提供了各自的 area() 实现。
    • 它们也重写了析构函数。
  3. main 函数:

    • vector<unique_ptr<Shape>> shapes;: 创建一个 vector 来存储指向 Shape 对象的智能指针。unique_ptr 是一种自动管理内存的智能指针,它会在对象生命周期结束时自动释放内存。
    • shapes.push_back(make_unique<Circle>(5.0));: 创建一个 Circle 对象,并将其包装在一个 unique_ptr 中,然后添加到 shapes 向量。注意,我们使用 make_unique 来创建智能指针。
    • shape_ptr->area(): 在循环中,我们通过基类指针 shape_ptr 来调用 area() 函数。因为 area() 是虚函数,C++ 运行时系统会根据 shape_ptr 指向的对象的实际类型(CircleRectangle)来决定调用哪个 area() 函数。这就是多态的体现。
    • main 函数结束时,shapes 向量中的 unique_ptr 会自动销毁它们所指向的对象。由于析构函数是虚函数,每个对象的正确析构函数(~Circle()~Rectangle())都会被调用。

多态的优势

  • 代码的灵活性和可扩展性: 你可以轻松地添加新的派生类,而无需修改现有使用基类指针的代码。
  • 简化复杂的数据结构: 允许你将不同类型的对象存储在同一个容器中(如 vector),并以统一的方式处理它们。

抽象类和纯虚函数

抽象类 抽象类(Abstract Class)是指那些至少包含一个纯虚函数的类。抽象类不能被实例化,也就是说,你不能直接创建抽象类的对象。它们的主要目的是作为基类,为派生类提供接口和共享的实现。

纯虚函数 纯虚函数(Pure Virtual Function)是一种虚函数,它在基类中被声明,但没有提供实现。纯虚函数使用 = 0 来定义。任何包含纯虚函数的类都成为抽象类。

示例:抽象类和纯虚函数

#include <iostream>
#include <vector>
#include <memory> // for std::unique_ptr

using namespace std;

// 抽象基类
class Animal {
public:
    // 纯虚函数,派生类必须实现
    virtual void makeSound() const = 0;

    // 虚析构函数
    virtual ~Animal() {
        cout << "Animal 析构函数被调用" << endl;
    }
};

// 派生类:狗
class Dog : public Animal {
public:
    void makeSound() const override {
        cout << "汪汪!" << endl;
    }

    ~Dog() override {
        cout << "Dog 析构函数被调用" << endl;
    }
};

// 派生类:猫
class Cat : public Animal {
public:
    void makeSound() const override {
        cout << "喵喵!" << endl;
    }

    ~Cat() override {
        cout << "Cat 析构函数被调用" << endl;
    }
};

int main() {
    // 不能直接创建抽象类的对象
    // Animal myAnimal; // 错误!

    vector<unique_ptr<Animal>> animals;

    animals.push_back(make_unique<Dog>());
    animals.push_back(make_unique<Cat>());
    animals.push_back(make_unique<Dog>());

    for (const auto& animal_ptr : animals) {
        animal_ptr->makeSound(); // 调用派生类的 makeSound()
    }

    return 0;
}

代码解释

  1. Animal 抽象类:

    • virtual void makeSound() const = 0;: 声明了一个纯虚函数 makeSound()= 0 表示这个函数没有实现,派生类必须提供自己的实现。
    • 由于 Animal 类包含纯虚函数,它是一个抽象类,不能被实例化。
  2. DogCat 派生类:

    • 它们都继承自 Animal
    • 它们都实现了 makeSound() 纯虚函数,提供了各自的叫声。
  3. main 函数:

    • 我们创建了一个存储 Animal 指针的 vector
    • 通过基类指针 animal_ptr 调用 makeSound(),实现了多态。

纯虚函数的意义

  • 强制派生类实现特定的接口。
  • 定义了类的“契约”,确保所有派生类都具有某些基本功能。

模板

模板是 C++ 中实现泛型编程的机制,它允许你编写独立于数据类型的代码。模板可以用于函数和类。

函数模板

函数模板允许你定义一个通用的函数,该函数可以处理不同类型的数据。

语法

template <typename T> // 或者 template <class T>
return_type function_name(parameters) {
    // 函数体
}
  • template <typename T>: 声明这是一个模板,T 是一个类型参数,代表一个占位符。
  • typename (或 class) 关键字用于声明类型参数。

示例:函数模板

#include <iostream>

// 定义一个函数模板,用于交换两个变量的值
template <typename T>
void swapValues(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

int main() {
    int i = 5, j = 10;
    cout << "交换前: i = " << i << ", j = " << j << endl;
    swapValues(i, j); // 编译器会根据 i 和 j 的类型推断 T 为 int
    cout << "交换后: i = " << i << ", j = " << j << endl;

    double d1 = 3.14, d2 = 2.71;
    cout << "交换前: d1 = " << d1 << ", d2 = " << d2 << endl;
    swapValues(d1, d2); // 编译器会推断 T 为 double
    cout << "交换后: d1 = " << d1 << ", d2 = " << d2 << endl;

    return 0;
}

在这个例子中,swapValues 函数模板可以用于交换 int 类型变量和 double 类型变量。编译器会根据传递的参数类型自动生成特定类型的函数。

类模板

类模板允许你定义一个通用的类,该类可以处理不同类型的数据成员。

语法

template <typename T> // 或者 template <class T>
class ClassName {
private:
    T member_variable;

public:
    // 构造函数、成员函数等
    ClassName(T val) : member_variable(val) {}

    T getMember() const {
        return member_variable;
    }
};

示例:类模板

#include <iostream>

// 定义一个类模板,用于存储一个任意类型的值
template <typename T>
class Box {
private:
    T value;

public:
    // 构造函数
    Box(T val) : value(val) {
        std::cout << "Box 对象创建,值为: " << value << std::endl;
    }

    // 获取存储的值
    T getValue() const {
        return value;
    }
};

int main() {
    // 创建一个存储 int 的 Box 对象
    Box<int> intBox(100);
    std::cout << "intBox 的值: " << intBox.getValue() << std::endl;

    // 创建一个存储 string 的 Box 对象
    Box<std::string> stringBox("Hello Template");
    std::cout << "stringBox 的值: " << stringBox.getValue() << std::endl;

    return 0;
}

在这个例子中,Box 类模板可以用来创建存储 intstd::string 的对象。在创建对象时,你需要明确指定模板参数,例如 Box<int>Box<std::string>

STL (Standard Template Library)

STL 是 C++ 标准库的一部分,提供了一系列常用的数据结构和算法,它们都是基于模板实现的。STL 的核心组件包括:

  1. 容器 (Containers): 用于存储数据的各种数据结构。
    • 序列容器:
      • vector: 动态数组,支持快速随机访问,在末尾插入/删除效率高。
      • deque: 双端队列,支持在两端高效插入/删除。
      • list: 双向链表,支持在任意位置高效插入/删除,但随机访问效率低。
      • forward_list: 单向链表,比 list 更节省空间。
      • array: 固定大小的数组。
    • 关联容器:
      • set: 存储唯一元素的有序集合。
      • multiset: 存储元素的有序集合,允许重复元素。
      • map: 存储键值对的有序映射,键是唯一的。
      • multimap: 存储键值对的有序映射,键可以重复。
    • 无序关联容器 (C++11 及以后):
      • unordered_set: 存储唯一元素的无序集合。
      • unordered_multiset: 存储元素的无序集合,允许重复元素。
      • unordered_map: 存储键值对的无序映射,键是唯一的。
      • unordered_multimap: 存储键值对的无序映射,键可以重复。
  2. 迭代器 (Iterators): 类似于指针,用于遍历容器中的元素。它们提供了一种统一的接口来访问不同容器中的数据。
  3. 算法 (Algorithms): 提供了一系列通用的算法,如排序、查找、拷贝、转换等,可以作用于容器中的元素。例如:
    • sort(): 对序列进行排序。
    • find(): 在序列中查找元素。
    • copy(): 拷贝序列中的元素。
    • for_each(): 对序列中的每个元素执行一个操作。
  4. 函数对象 (Function Objects): 也称为仿函数,是重载了函数调用运算符 () 的类。它们可以像函数一样被调用,并且可以携带状态。
  5. 适配器 (Adapters): 用于修改现有容器或迭代器的接口,使其满足特定需求。例如:
    • stack: LIFO (后进先出) 的栈。
    • queue: FIFO (先进先出) 的队列。
    • priority_queue: 优先队列。

示例:使用 vectorsort

#include <iostream>
#include <vector>
#include <algorithm> // for std::sort
#include <string>

int main() {
    // 创建一个存储 int 的 vector
    std::vector<int> numbers = {5, 2, 8, 1, 9, 4};

    // 使用 sort 算法对 vector 进行排序
    std::sort(numbers.begin(), numbers.end());

    // 遍历并打印排序后的元素
    std::cout << "排序后的数字: ";
    for (int num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    // 创建一个存储 string 的 vector
    std::vector<std::string> words = {"banana", "apple", "cherry", "date"};

    // 使用 sort 算法对 string vector 进行排序
    std::sort(words.begin(), words.end());

    // 遍历并打印排序后的字符串
    std::cout << "排序后的字符串: ";
    for (const std::string& word : words) {
        std::cout << word << " ";
    }
    std::cout << std::endl;

    return 0;
}

在这个例子中:

  • std::vector<int> numbers = {5, 2, 8, 1, 9, 4}; 创建了一个 vector 并初始化了一些整数。
  • std::sort(numbers.begin(), numbers.end()); 调用了 STL 的 sort 算法,对 numbers 向量中的元素进行升序排序。numbers.begin()numbers.end() 返回指向 vector 开始和结束位置的迭代器。
  • for (int num : numbers) 是 C++11 引入的范围-based for 循环,方便地遍历 vector 中的每个元素。

STL 极大地提高了 C++ 开发的效率和代码质量,是现代 C++ 开发中不可或缺的一部分。

贡献者: zongxi