大二上 C++高级程序设计期末复习 20241228

1.结合简单例子讲解c++面向对象里的构造函数,拷贝构造函数,析构函数,普通成员函数

在C++中,面向对象编程(OOP)是一种编程范式,它使用类和对象来模拟现实世界中的实体和行为。以下是构造函数、拷贝构造函数、析构函数和普通成员函数的简单解释和例子:

1. 构造函数

构造函数是一种特殊的成员函数,用于创建对象时初始化对象的状态。它与类同名,并且没有返回类型,甚至连void也不行。

例子:

class Person {
public:
    // 构造函数
    Person(string name, int age) {
        this->name = name;
        this->age = age;
    }

private:
    string name;
    int age;
};

// 使用
Person p1("Alice", 30);

2. 拷贝构造函数

拷贝构造函数用于创建一个对象作为另一个同类型对象的副本。它的第一个参数是该类类型的引用,并且通常被标记为常量引用,以避免自我赋值时的无限递归。

例子:

class Person {
public:
    // 拷贝构造函数
    Person(const Person& other) {
        this->name = other.name;
        this->age = other.age;
    }

private:
    string name;
    int age;
};

// 使用
Person p1("Alice", 30);
Person p2 = p1; // 使用拷贝构造函数

3. 析构函数

析构函数用于对象生命周期结束时执行清理工作,比如释放资源。它与构造函数相反,没有参数,没有返回值,并且不能被重写。

例子:

class Person {
public:
    // 析构函数
    ~Person() {
        cout << "Person is being destroyed" << endl;
    }

private:
    string name;
    int age;
};

// 使用
Person p1("Alice", 30);
// 当p1离开作用域时,析构函数被调用

4. 普通成员函数

普通成员函数是类中定义的函数,用于实现类的行为。它们可以访问类的私有成员。

例子:

class Person {
public:
    // 构造函数
    Person(string name, int age) : name(name), age(age) {}

    // 普通成员函数
    void introduce() {
        cout << "Hello, my name is " << name << " and I am " << age << " years old." << endl;
    }

private:
    string name;
    int age;
};

// 使用
Person p1("Alice", 30);
p1.introduce(); // 输出: Hello, my name is Alice and I am 30 years old.

在这些例子中,Person类有三个成员:一个构造函数,一个普通成员函数introduce,和一个私有成员变量nameage。构造函数用于初始化这些成员变量,拷贝构造函数用于创建一个Person对象的副本,析构函数用于在对象销毁时执行清理工作,而introduce是一个普通成员函数,用于输出对象的介绍信息。

2.C++面向对象编程中的static

在C++中,static关键字可以用于多个上下文,包括静态成员变量、静态成员函数、静态类成员初始化以及局部静态变量。下面分别结合简单的例子来解释这些用途。

1. 静态成员变量

静态成员变量是属于类的,而不是属于类的某个特定对象的。这意味着所有对象共享同一个静态成员变量。

class MyClass {
public:
    static int staticVar; // 声明静态成员变量
};

// 静态成员变量需要在类外进行定义和初始化
int MyClass::staticVar = 0;

void exampleFunction() {
    MyClass obj1, obj2;

    obj1.staticVar = 10; // 所有对象共享这个变量
    std::cout << obj2.staticVar; // 输出10,因为obj1和obj2共享同一个staticVar
}

2. 静态成员函数

静态成员函数是属于类的,它们可以访问类的静态成员变量,但不能访问非静态成员变量。

class MyClass {
public:
    static int staticVar;

    static void staticFunction() {
        staticVar = 20; // 静态成员函数可以访问静态成员变量
        // nonStaticVar = 30; // 错误:静态成员函数不能访问非静态成员变量
    }
};

int MyClass::staticVar = 0;

void exampleFunction() {
    MyClass::staticFunction(); // 直接通过类名调用静态成员函数
    std::cout << MyClass::staticVar; // 输出20
}

3. 静态类成员初始化

从C++11开始,可以在类内直接初始化静态成员变量。

class MyClass {
public:
    static int staticVar = 10; // C++11允许在类内直接初始化静态成员变量
};

void exampleFunction() {
    std::cout << MyClass::staticVar; // 输出10
}

4. 局部静态变量

在函数内部声明的静态变量,它们的生命周期贯穿整个程序,只在第一次调用函数时初始化。

int exampleFunction() {
    static int localStaticVar = 30; // 局部静态变量
    localStaticVar += 10;
    return localStaticVar;
}

void callFunction() {
    std::cout << exampleFunction(); // 第一次调用,输出40
    std::cout << exampleFunction(); // 第二次调用,输出50,因为localStaticVar没有被重置
}

在这些例子中,static关键字用于控制变量和函数的存储和访问方式,使得它们可以在不同的上下文中以不同的方式被使用。

3.C++面向对象编程中的友元

在C++中,友元(friend)是一种特殊的类成员,它可以是一个函数或者是一个类。友元不是类的成员,但它可以访问类的私有(private)和保护(protected)成员。这使得友元函数能够“看到”类的内部数据,就像它们是类的成员一样。

友元函数

友元函数可以是另一个类的成员函数,也可以是普通函数。当一个函数被声明为一个类的友元时,它可以访问该类的所有私有和保护成员。

class MyClass {
private:
    int secret;

public:
    MyClass(int val) : secret(val) {}

    // 声明友元函数
    friend void accessSecret(MyClass obj);
};

// 友元函数可以访问MyClass的私有成员
void accessSecret(MyClass obj) {
    std::cout << "The secret is: " << obj.secret << std::endl;
}

int main() {
    MyClass obj(42);
    accessSecret(obj); // 输出:The secret is: 42
    return 0;
}

在这个例子中,accessSecret函数被声明为MyClass的友元,因此它可以访问MyClass的私有成员secret

友元类

友元类的所有成员函数都可以访问另一个类的私有和保护成员。

class MyClass {
private:
    int secret;

public:
    MyClass(int val) : secret(val) {}

    // 声明友元类
    friend class FriendClass;
};

class FriendClass {
public:
    void accessSecret(MyClass& obj) {
        std::cout << "The secret is: " << obj.secret << std::endl;
    }
};

int main() {
    MyClass obj(42);
    FriendClass friendObj;
    friendObj.accessSecret(obj); // 输出:The secret is: 42
    return 0;
}

在这个例子中,FriendClass被声明为MyClass的友元类,因此FriendClass中的任何成员函数都可以访问MyClass的私有成员secret

注意事项

  • 友元关系不是相互的。如果类A是类B的友元,并不意味着类B自动成为类A的友元。
  • 友元关系破坏了封装性,因为友元可以访问类的私有和保护成员。因此,应该谨慎使用友元,只在确实需要时使用。
  • 友元函数不是类的成员函数,所以它们不能访问类的this指针。
  • 在多重继承的情况下,友元关系可能变得复杂,需要仔细处理友元的访问权限。

友元是C++中一个强大的特性,它允许在保持类的封装性的同时,提供对类内部数据的访问。然而,由于它破坏了封装性,所以在设计类和接口时应该谨慎使用。

4.C++面向对象编程中的虚函数与多态性

在C++中,虚函数和多态性是面向对象编程的核心概念。虚函数允许在派生类中重写(override)基类中的函数,而多态性则是指同一个函数调用可以有不同的行为,这取决于对象的实际类型。

虚函数

虚函数是在基类中使用virtual关键字声明的函数,它允许在派生类中被重写。这是实现多态性的基础。

class Base {
public:
    virtual void show() {  // 虚函数
        std::cout << "Base show()" << std::endl;
    }
    virtual ~Base() {}  // 虚析构函数以确保派生类对象的正确析构
};

class Derived : public Base {
public:
    void show() override {  // 重写基类的虚函数
        std::cout << "Derived show()" << std::endl;
    }
};

多态性

多态性分为编译时多态(静态多态)和运行时多态(动态多态)。虚函数实现的是运行时多态。

运行时多态(动态多态)

运行时多态是指程序在运行时根据对象的实际类型来决定调用哪个函数。这通常通过基类的指针或引用来实现。

int main() {
    Base* b = new Base();
    Base* d = new Derived();

    b->show();  // 输出:Base show()
    d->show();  // 输出:Derived show()

    delete b;
    delete d;
    return 0;
}

在这个例子中,bd都是Base类型的指针,但是d指向的实际上是Derived类型的对象。当我们调用show()函数时,由于d指向的是Derived类型的对象,所以调用的是Derived类的show()函数。这就是多态性:同一个函数调用,根据对象的实际类型,执行不同的代码。

编译时多态(静态多态)

编译时多态通常通过函数重载(函数名相同,参数列表不同)和运算符重载来实现。

class Base {
public:
    void show(int) {  // 函数重载
        std::cout << "Base show(int)" << std::endl;
    }
};

class Derived : public Base {
public:
    void show(int) override {  // 重写基类的函数
        std::cout << "Derived show(int)" << std::endl;
    }
    void show(double) {  // 运算符重载
        std::cout << "Derived show(double)" << std::endl;
    }
};

int main() {
    Derived d;
    d.show(1);  // 输出:Derived show(int)
    d.show(1.0);  // 输出:Derived show(double)
    return 0;
}

在这个例子中,show()函数在BaseDerived类中被重载,编译器根据传递的参数类型来决定调用哪个函数。

虚析构函数

在含有虚函数的类中,通常需要一个虚析构函数,以确保通过基类指针删除派生类对象时,能够正确地调用派生类的析构函数,完成资源的清理。

class Base {
public:
    virtual ~Base() {
        std::cout << "Base destructor" << std::endl;
    }
};

class Derived : public Base {
public:
    ~Derived() {
        std::cout << "Derived destructor" << std::endl;
    }
};

int main() {
    Base* d = new Derived();
    delete d;  // 首先输出:Derived destructor,然后输出:Base destructor
    return 0;
}

在这个例子中,如果没有虚析构函数,那么删除d时将不会调用Derived的析构函数,可能导致资源泄漏。

通过虚函数和多态性,C++允许我们编写更灵活和可扩展的代码,使得我们可以编写出更加通用和可重用的组件。

多态使用的样例:

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

//动物类
class Animal
{
public:
	virtual void speak(){
		cout << "Animal的speak()方法" << endl;
	}
};

//定义一个People类,继承Animal
class People : public Animal
{
public:
	void speak(){
		cout << "People的speak()方法" << endl;
	}
};

//定义一个Dog类,继承Animal
class Dog : public Animal
{
public:
	void speak(){
		cout << "Dog的speak()方法" << endl;
	}
};

int main()
{
	//声明一个 Animal 的指针对象,并没有实例化
	Animal *animal; 
	People people;
	Dog dog;
	//存储dog和people对象的地址,并调用speak()方法
	animal = &people;
	animal->speak();
	
	animal = &dog;
	animal->speak();

	return 0;
}

5.C++中的模板

在C++中,模板是一种支持泛型编程的机制,允许编写与数据类型无关的代码。模板可以用于函数(函数模板)和类(类模板)。

函数模板

函数模板是一种允许函数接受任何类型参数的模板。这样,同一个函数可以用于不同的数据类型。

// 函数模板示例:交换两个值
template <typename T>
void swap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 5;
    int y = 10;
    swap(x, y); // 调用 int 类型的 swap

    double fx = 5.5;
    double fy = 10.5;
    swap(fx, fy); // 调用 double 类型的 swap

    std::string s1 = "Hello";
    std::string s2 = "World";
    swap(s1, s2); // 调用 std::string 类型的 swap

    return 0;
}

在这个例子中,swap函数模板可以用于任何类型的参数,无论是intdouble还是std::string。编译器在编译时会根据传入的参数类型生成相应的函数实现。

类模板

类模板是一种支持创建与数据类型无关的类的模板。这样,同一个类可以用于不同的数据类型。

// 类模板示例:一个简单的栈
template <typename T>
class Stack {
private:
    T* data;      // 存储元素的数组
    size_t size; // 栈的当前大小
    size_t capacity; // 栈的容量

public:
    Stack(size_t cap) : size(0), capacity(cap) {
        data = new T[capacity];
    }

    ~Stack() {
        delete[] data;
    }

    void push(const T& value) {
        if (size < capacity) {
            data[size++] = value;
        }
    }

    T pop() {
        if (size == 0) {
            throw std::out_of_range("Stack<>::pop(): empty stack");
        }
        return data[--size];
    }

    size_t getSize() const {
        return size;
    }
};

int main() {
    Stack<int> intStack(10); // 创建 int 类型的栈
    Stack<double> doubleStack(10); // 创建 double 类型的栈

    intStack.push(1);
    intStack.push(2);

    std::cout << intStack.pop() << std::endl; // 输出 2

    doubleStack.push(3.14);
    doubleStack.push(2.71);

    std::cout << doubleStack.pop() << std::endl; // 输出 2.71

    return 0;
}

在这个例子中,Stack类模板可以用于任何类型的数据。我们创建了int类型的栈和double类型的栈,它们都使用相同的Stack类模板,但是存储不同类型的数据。

注意事项

  • 模板参数typename可以省略,直接写作template <class T>template <typename T>
  • 模板可以有多个参数,例如template <typename T, typename U>
  • 模板的特化(specialization)和偏特化(partial specialization)允许为特定的类型或类型组合提供特定的实现。
  • 模板元编程(Template Metaprogramming)是一种利用模板在编译时执行计算的技术。
  • 模板代码应该避免在头文件中定义复杂的逻辑,因为它们会导致编译时间增加。

函数模板和类模板是C++泛型编程的基础,它们使得代码更加通用和重用性更强。通过使用模板,我们可以编写出更加灵活和高效的代码。

6.智能指针

在C++中,智能指针是一种自动管理动态分配内存的类模板,它们在对象不再使用时自动释放内存,从而帮助防止内存泄漏。C++标准库提供了几种智能指针,包括std::unique_ptrstd::shared_ptrstd::weak_ptr。下面我将通过简单的例子来详细讲解这些智能指针的使用方法。

std::unique_ptr

std::unique_ptr代表独一无二的对象所有权,不允许复制,只允许移动。它确保指针离开作用域时自动释放资源。

#include <iostream>
#include <memory>

class Resource {
public:
    Resource() { std::cout << "Resource created\n"; }
    ~Resource() { std::cout << "Resource destroyed\n"; }
};

int main() {
    std::unique_ptr<Resource> ptr1(new Resource()); // 创建一个Resource对象
    // ptr1 = ptr2; // 错误:unique_ptr不允许复制

    if (ptr1) {
        std::cout << "Resource is being used\n";
    }

    // 不需要delete,离开作用域时自动释放
    return 0;
}

std::shared_ptr

std::shared_ptr代表多个对象共享所有权,使用引用计数机制来管理对象生命周期。当最后一个shared_ptr被销毁时,对象会被自动释放。

#include <iostream>
#include <memory>

class Resource {
public:
    Resource() { std::cout << "Resource created\n"; }
    ~Resource() { std::cout << "Resource destroyed\n"; }
};

int main() {
    std::shared_ptr<Resource> ptr1(new Resource());
    std::shared_ptr<Resource> ptr2 = ptr1; // 共享同一个Resource对象

    if (ptr1 && ptr2) {
        std::cout << "Resource is being shared\n";
    }

    // 不需要delete,最后一个shared_ptr离开作用域时自动释放
    return 0;
}

std::weak_ptr

std::weak_ptr是一种不控制对象生命周期的智能指针,它用来观察std::shared_ptr管理的对象,而不会改变对象的引用计数。这在循环引用的情况下非常有用。

#include <iostream>
#include <memory>

class Resource {
public:
    Resource() { std::cout << "Resource created\n"; }
    ~Resource() { std::cout << "Resource destroyed\n"; }
};

int main() {
    std::shared_ptr<Resource> ptr1(new Resource());
    std::weak_ptr<Resource> weakPtr = ptr1; // 观察ptr1管理的Resource对象

    if (weakPtr.lock()) {
        std::cout << "Resource is being observed\n";
    }

    ptr1.reset(); // 显式释放ptr1管理的Resource对象

    if (weakPtr.expired()) { // 检查weakPtr是否还有效
        std::cout << "Resource is no longer available\n";
    }

    return 0;
}

在这个例子中,weakPtr是一个std::weak_ptr,它观察ptr1管理的Resource对象。当ptr1reset后,weakPtr不再指向一个有效的对象,weakPtr.expired()返回true

注意事项

  • 智能指针不应该被用在具有大量小对象和短生命周期的场景中,因为它们会增加额外的开销。
  • 智能指针不应该用于管理栈上的自动存储期对象或全局/静态存储期对象。
  • 智能指针不应该被用在需要精确控制对象生命周期的场景中,比如对象需要在特定的时间点被释放。

智能指针是C++中管理动态内存的强大工具,它们简化了资源管理,减少了内存泄漏的风险。

7.指针函数VS函数指针

在C++中,指针函数(Pointer to Function)和函数指针(Function Pointer)是两种与函数相关的指针类型,它们在不同的场景下有着不同的用途。下面通过简单的例子来解释它们的区别和用法。

指针函数(Pointer to Function)

指针函数是指函数的返回类型是指针类型。这种函数可以返回一个指向某个数据类型的指针。

#include <iostream>

// 指针函数,返回一个int类型的指针
int* pointerFunction() {
    int value = 10; // 局部变量
    return &value; // 返回局部变量的地址(注意:这是一个危险操作,因为value的生命周期只在函数内部)
}

int main() {
    int* ptr = pointerFunction();
    std::cout << *ptr << std::endl; // 输出10,但是这样做是不安全的,因为pointerFunction中的value已经销毁了
    return 0;
}

在上面的例子中,pointerFunction是一个返回int*类型的函数。它返回了一个局部变量的地址,这是不安全的,因为局部变量在函数返回后就会被销毁,所以这个指针指向的是一个未定义的值。

函数指针(Function Pointer)

函数指针是一个指向函数的指针,它可以用来存储函数的地址,并且可以在需要的时候调用这个函数。

#include <iostream>

// 一个简单的函数
void printNumber(int number) {
    std::cout << number << std::endl;
}

// 函数指针
void (*functionPointer)(int);

int main() {
    functionPointer = printNumber; // 将函数的地址赋给函数指针

    // 通过函数指针调用函数
    functionPointer(42); // 输出42

    // 也可以直接使用函数名调用
    printNumber(42); // 输出42

    return 0;
}

在这个例子中,functionPointer是一个函数指针,它的类型是指向一个接受int参数且没有返回值的函数。我们把printNumber函数的地址赋给了functionPointer,然后通过functionPointer来调用printNumber函数。

总结

  • 指针函数:返回指针类型的函数。
  • 函数指针:指向函数的指针,可以用来存储和调用函数。

函数指针在C++中非常有用,它们可以作为参数传递给其他函数,也可以作为函数的返回值,这使得函数的调用更加灵活。而指针函数则更多地用于返回对象的地址,但需要注意返回值的生命周期问题,以避免悬空指针的风险。

8.成员初始化列表

成员初始化列表在C++中非常有用,特别是在需要初始化而不是赋值成员变量时。下面我将通过一个简单的例子来解释成员初始化列表的用法。
成员初始化列表(Member Initializer List)是C++中的一个特性,它允许你在构造函数中直接初始化类的成员变量。这是一种比在构造函数体中赋值更高效、更清晰的方法,尤其是对于基本数据类型和对象的初始化。

为什么使用成员初始化列表?
效率:成员初始化列表直接初始化成员变量,而不是先默认构造后再赋值,这样可以提高效率。
必要性:对于常量成员和引用成员,必须使用成员初始化列表,因为它们不能被赋值,只能被初始化。
清晰性:成员初始化列表使得构造函数的意图更加明确,代码更易于阅读。
成员初始化列表的语法
成员初始化列表的语法如下:

cpp
ClassName::ClassName(parameters) : member1(value1), member2(value2), ... {}
ClassName 是类的名称。
parameters 是构造函数的参数。
member1, member2, ... 是类的成员变量。
value1, value2, ... 是用于初始化成员变量的值。
简单例子
假设我们有一个 Point 类,它有两个成员变量 x 和 y,分别表示点的横坐标和纵坐标。

不使用成员初始化列表
cpp
class Point {
public:
int x;
int y;

Point(int x, int y) {
    this->x = x; // 赋值
    this->y = y; // 赋值
}

};
在这个例子中,构造函数通过赋值来初始化成员变量 x 和 y。

示例:使用成员初始化列表初始化 Point

假设我们有一个 Point 类,它有两个成员变量 xy,我们希望在创建 Point 对象时直接初始化这些变量。

#include <iostream>

class Point {
public:
    int x;
    int y;

    // 使用成员初始化列表初始化 x 和 y
    Point(int xVal, int yVal) : x(xVal), y(yVal) {
        // 构造函数体可以留空,因为初始化已经通过成员初始化列表完成
    }

    // 成员函数,用于打印点的坐标
    void print() const {
        std::cout << "Point(" << x << ", " << y << ")" << std::endl;
    }
};

int main() {
    // 使用成员初始化列表创建 Point 对象
    Point point1(3, 4); // x 被初始化为 3,y 被初始化为 4
    Point point2(6, 8); // x 被初始化为 6,y 被初始化为 8

    // 打印点的坐标
    point1.print(); // 输出 "Point(3, 4)"
    point2.print(); // 输出 "Point(6, 8)"

    return 0;
}

在这个例子中,Point 类的构造函数使用了成员初始化列表来初始化 xy。这是通过在构造函数的参数列表后面跟随一个冒号(:)和初始化列表来完成的。在初始化列表中,我们指定了每个成员变量和对应的值。

成员初始化列表的优点

  1. 效率:成员初始化列表直接初始化成员变量,而不是先默认构造后再赋值,这样可以提高效率。
  2. 必要性:对于常量成员和引用成员,必须使用成员初始化列表,因为它们不能被赋值,只能被初始化。
  3. 清晰性:成员初始化列表使得构造函数的意图更加明确,代码更易于阅读。

另一个例子:使用常量和引用成员

让我们考虑一个包含常量成员和引用成员的类,这将展示成员初始化列表的必要性。

#include <iostream>
#include <string>

class Person {
public:
    const std::string name; // 常量成员
    std::string& nickname;  // 引用成员

    // 使用成员初始化列表初始化 name 和 nickname
    Person(const std::string& name, const std::string& nickname)
        : name(name), nickname(nickname) {}

    // 成员函数,用于打印人的名字和昵称
    void print() const {
        std::cout << "Name: " << name << ", Nickname: " << nickname << std::endl;
    }
};

int main() {
    std::string nickname = "Kimi";
    Person person("Moonshot", nickname); // 使用成员初始化列表创建 Person 对象

    // 打印人的名字和昵称
    person.print(); // 输出 "Name: Moonshot, Nickname: Kimi"

    return 0;
}

在这个例子中,Person 类有一个常量成员 name 和一个引用成员 nickname。由于 name 是常量,它必须在构造函数中被初始化,而 nickname 是引用,它也必须在构造函数中被初始化。成员初始化列表是完成这一任务的正确方式。

通过这两个例子,你可以看到成员初始化列表在C++中的重要性和实用性。

9.操作符重载

C++中的操作符重载是一种允许程序员定义操作符(如 +, -, *, /, == 等)在特定类的对象上的行为的机制。这使得自定义类型的对象可以像内置类型一样使用这些操作符,从而提高代码的可读性和表达力。

操作符重载的基本规则

  1. 大多数操作符都可以被重载,除了 .(成员访问)、::(作用域解析)、.*->*(成员指针访问)。
  2. 操作符重载不是继承的,每个类必须独立地重载操作符。
  3. 操作符重载通常应该是类的成员函数,除了赋值操作符(=)、下标操作符([])和函数调用操作符(()`)可以是非成员函数。

简单例子:重载加法操作符

假设我们有一个 Point 类,表示二维空间中的点,我们希望使用 + 操作符来实现两个点的坐标相加。

#include <iostream>

class Point {
public:
    int x, y;

    // 构造函数
    Point(int x, int y) : x(x), y(y) {}

    // 重载加法操作符
    Point operator+(const Point& rhs) const {
        return Point(x + rhs.x, y + rhs.y);
    }

    // 用于打印点的坐标
    void print() const {
        std::cout << "(" << x << ", " << y << ")" << std::endl;
    }
};

int main() {
    Point p1(1, 2);
    Point p2(3, 4);
    Point p3 = p1 + p2; // 使用重载的加法操作符
    p3.print(); // 输出 (4, 6)

    return 0;
}

在这个例子中,我们重载了 + 操作符,使其接受两个 Point 对象作为参数,并返回一个新的 Point 对象,其坐标是两个输入点的坐标之和。这样,我们就可以使用 + 操作符来直接相加两个 Point 对象。

重载赋值操作符

赋值操作符通常需要特别小心,因为它需要处理自赋值的情况。

class Point {
public:
    int x, y;

    Point(int x, int y) : x(x), y(y) {}

    // 重载赋值操作符
    Point& operator=(const Point& rhs) {
        if (this != &rhs) { // 检查自赋值
            x = rhs.x;
            y = rhs.y;
        }
        return *this;
    }

    void print() const {
        std::cout << "(" << x << ", " << y << ")" << std::endl;
    }
};

int main() {
    Point p1(1, 2);
    Point p2 = p1; // 默认赋值操作符
    Point p3;
    p3 = p2; // 使用重载的赋值操作符
    p3.print(); // 输出 (1, 2)

    return 0;
}

在这个例子中,我们重载了赋值操作符 =。我们首先检查是否是自赋值(即左边和右边的对象是否是同一个),如果不是,我们才进行赋值操作。最后,我们返回当前对象的引用,以便可以链式赋值。

注意事项

  • 重载操作符应该保持操作符原有的语义,例如,重载 + 应该意味着“相加”,重载 == 应该意味着“相等”。
  • 重载操作符应该保持操作符原有的优先级和结合性
  • 对于非成员函数的操作符重载,应该在类定义的外部定义,并且至少有一个参数是该类的类型。

通过这些例子,可以看到操作符重载如何增强C++代码的表达力和灵活性。

posted @ 2024-12-28 13:42  陆舟LandBoat  阅读(53)  评论(0)    收藏  举报