1.3 面向对象 C++面试问题

时间:2024-10-30 08:48:03

1.3.1 简述一下什么是面向对象,面向对象与面向过程的区别

什么是面向对象

面向对象(Object-Oriented Programming,OOP)是一种编程范式,它通过将现实世界中的实体抽象为“对象”来组织代码。面向对象编程关注对象及其交互,而不仅仅是处理数据和函数。OOP的主要特征包括:

  1. 对象:对象是类的实例,封装了数据和操作这些数据的方法。每个对象都有自己的状态和行为。

  2. :类是对象的模板或蓝图,定义了对象的属性和方法。通过类,可以创建多个对象。

  3. 封装:封装是将数据(属性)和对数据的操作(方法)结合在一起,限制外部直接访问对象的内部状态,以保护数据。

  4. 继承:继承是允许新类从已有类继承属性和方法的机制,有助于代码复用和建立层次关系。

  5. 多态:多态允许不同类型的对象以相同的方式响应相同的消息,从而实现灵活性和可扩展性。

面向对象与面向过程的区别

面向对象编程和面向过程编程是两种不同的编程范式,它们在设计理念、结构和实现方式上存在显著区别:

特点 面向对象 (OOP) 面向过程 (POP)
基本单位 对象(class和object的组合) 函数和过程(procedure)
关注点 数据及其操作的封装,强调对象之间的交互 功能和步骤的执行,强调过程的顺序和控制流
设计方法 通过创建对象来建模现实世界,通过对象间的交互完成任务 通过功能模块来处理数据,强调逻辑和控制流
代码重用 通过继承和多态实现代码的复用 通过函数调用和模块化实现代码的复用
状态管理 对象的状态封装在对象内部,外部通过方法访问和修改 状态由全局变量或参数传递,容易导致数据共享和状态混乱
灵活性和扩展性 易于扩展和修改,通过继承和多态支持变化 修改和扩展可能需要更改多个函数,灵活性较差
设计复杂度 对象和类的设计可以处理更复杂的系统 简单系统容易实现,但复杂系统可能导致代码混乱

总结

面向对象编程通过将数据和操作封装在对象中,提供了更灵活和可维护的方式来开发软件,而面向过程编程则更关注于实现具体的步骤和过程。根据项目的需求和复杂性,开发者可以选择适合的编程范式。

1.3.2 简述一下面向对象的三大特征

面向对象编程(OOP)的三大特征是封装继承多态。这些特征构成了面向对象编程的核心,帮助开发者更好地组织代码、提高代码的可重用性和可维护性。

总结

  • 封装:隐藏内部实现,通过公开接口操作对象,保护数据并提高安全性。
  • 继承:通过继承基类的属性和方法,重用代码并实现类的扩展。
  • 多态:通过统一的接口表现不同的行为,增加程序的灵活性和可扩展性。

1. 封装(Encapsulation)

封装是指将对象的属性(数据)和方法(操作)组合在一起,并将这些细节隐藏起来,仅通过公开的接口(方法)来访问和修改对象的状态。封装的核心思想是“隐藏内部实现,暴露外部接口”,它主要通过访问控制(如privateprotectedpublic)来实现。

  • 优点
    • 提高代码的安全性,避免外部直接修改对象的内部状态。
    • 提供清晰的接口,简化了对象的使用,增强了模块化。
    • 使代码更易于维护和修改,内部实现的改变不会影响外部代码。

示例

class Car {
private:
    int speed;  // 速度为私有成员
public:
    void setSpeed(int s) {  // 通过公有方法修改速度
        speed = s;
    }
    int getSpeed() {  // 通过公有方法获取速度
        return speed;
    }
};

2. 继承(Inheritance)

继承是指通过从已有类(基类/父类)中派生出新类(派生类/子类),从而继承基类的属性和方法。继承允许代码的重用,避免重复编写相同的代码,并且可以通过派生类对基类进行扩展和定制。

  • 优点
    • 实现代码复用,减少重复代码。
    • 建立类之间的层次结构,体现“是一个”的关系(如“汽车是一个交通工具”)。
    • 派生类可以通过重写基类方法实现多态。

示例

class Vehicle {
public:
    void start() {  // 基类中的方法
        cout << "Vehicle started" << endl;
    }
};

class Car : public Vehicle {  // Car继承自Vehicle
public:
    void start() {  // 子类重写基类方法
        cout << "Car started" << endl;
    }
};

3. 多态(Polymorphism)

多态是指同一操作在不同对象上可以表现出不同的行为。在C++中,多态主要通过函数重载运算符重载虚函数实现。多态允许我们用相同的接口处理不同类型的对象,提高了程序的灵活性和扩展性。

  • 静态多态(编译时多态):通过函数重载和运算符重载实现,不同的参数类型会调用不同的函数。
  • 动态多态(运行时多态):通过虚函数实现,派生类可以根据实际对象类型重写基类的方法,运行时根据对象的实际类型调用相应的方法。

优点

  • 增强程序的灵活性,使代码更具可扩展性。
  • 提供统一接口,便于处理不同类型的对象。

示例

class Animal {
public:
    virtual void sound() {  // 基类中定义虚函数
        cout << "Some animal sound" << endl;
    }
};

class Dog : public Animal {
public:
    void sound() override {  // 子类重写虚函数
        cout << "Bark" << endl;
    }
};

class Cat : public Animal {
public:
    void sound() override {  // 子类重写虚函数
        cout << "Meow" << endl;
    }
};

1.3.3 简述一下C++的重载和重写,以及它们的区别

在C++中,重载(Overloading)和重写(Overriding)是两种不同的编程技术,尽管它们在某些方面类似,但它们的用途和规则是不同的。

1. 重载与重写的区别

区别点 重载(Overloading) 重写(Overriding)
作用域 发生在同一作用域中(即同一个类中)。 发生在继承关系中(即子类中重写父类的方法)。
函数签名 函数签名必须不同(参数类型、数量、顺序不同)。 函数签名必须完全相同(包括参数类型、数量、顺序和返回类型)。
关键字 不需要使用特殊关键字。 基类函数必须是虚函数(用virtual关键字),子类可以用override明示重写。
调用时的选择方式 编译时根据参数列表选择调用哪个函数(静态绑定)。 运行时根据对象类型选择调用哪个函数(动态绑定)。
应用场景 用于实现函数的多种不同实现,处理不同类型或数量的参数。 用于多态性,派生类提供基类虚函数的自定义实现。
返回类型 返回类型可以不同。 返回类型必须与被重写的基类虚函数相同。

2. 总结

  • 重载:是在同一作用域中定义同名但参数不同的多个函数,它用于处理不同类型或数量的参数,是一种编译时的多态性
  • 重写:是在继承关系中,子类重写父类的虚函数,它用于实现运行时的多态性,使得通过基类指针或引用调用时执行派生类的函数。

重载和重写的主要区别在于作用范围、函数签名的要求和调用时的绑定方式。


3. 重载(Overloading)

重载指的是同一作用域中定义多个同名函数,但它们的参数类型、数量或顺序不同。编译器根据调用时提供的参数列表来选择调用哪个函数。这种技术可以提高代码的可读性和灵活性。

特点:
  • 同一个作用域中,可以定义多个同名函数。
  • 函数的参数数量、类型或顺序必须不同(与返回类型无关)。
  • 可以应用于普通函数运算符(运算符重载)。
示例:
class Calculator {
public:
    int add(int a, int b) {  // 参数为两个int
        return a + b;
    }

    double add(double a, double b) {  // 参数为两个double
        return a + b;
    }
};

在这个例子中,add函数被重载,可以处理不同类型的参数(intdouble)。

4. 重写(Overriding)

重写指的是在派生类中重新定义基类中的虚函数,以便派生类能够提供其自身的实现。重写通常用于多态性,使基类的指针或引用可以调用派生类中重写的函数。

特点:
  • 基类中的函数必须用**virtual关键字**声明为虚函数。
  • 派生类中的函数签名(函数名、参数列表和返回类型)必须与基类中的虚函数完全相同。
  • 重写发生在继承关系中,即子类重写父类的虚函数。
  • 通过基类的指针或引用调用重写的函数时,会执行派生类的实现(动态绑定)。
示例:
class Animal {
public:
    virtual void makeSound() {  // 基类中的虚函数
        cout << "Animal sound" << endl;
    }
};

class Dog : public Animal {
public:
    void makeSound() override {  // 派生类中重写虚函数
        cout << "Bark" << endl;
    }
};

在这个例子中,makeSound函数在Dog类中重写了基类Animal的实现。如果通过基类指针调用makeSound,运行时将执行Dog类的版本。

1.3.4 说说C++的重载和重写是如何实现的

总结与对比

特性 重载(Overloading) 重写(Overriding)
实现机制 编译时通过函数名修饰(Name Mangling)实现 通过虚函数表(vtable)和虚表指针(vptr)在运行时实现
绑定类型 静态绑定(编译时确定) 动态绑定(运行时确定)
作用范围 同一类内,不涉及继承 继承体系中,子类重写基类的虚函数
性能开销 无运行时开销,编译时决定 运行时有一定的性能开销(通过虚表指针查找虚函数表,动态调用)
目的 提供同名函数的多个版本以支持不同的参数类型或数量 实现多态性,通过基类指针调用派生类的方法

重载(Overloading)

重载是通过编译时的静态绑定实现的,即在编译时,编译器通过函数名修饰(Name Mangling)来生成唯一的函数标识符,以区分同名的不同函数。重载的目的是在同一个作用域内提供同名函数的多个版本,支持不同的参数类型或数量,从而提高代码的灵活性和可读性。

  • 静态绑定:重载函数的选择在编译时完成,编译器根据参数列表决定调用哪个版本的函数。
  • 函数签名差异:函数名相同,但参数类型、数量或顺序不同。
  • 无继承关系:重载发生在同一类的作用域内,不涉及继承关系。

重写(Overriding)

重写是通过运行时的动态绑定实现的。它依赖于C++的虚函数机制,即基类中的虚函数可以在派生类中被重新定义,从而实现多态性。虚函数表(vtable)和虚表指针(vptr)是重写的核心机制,通过它们在运行时决定实际调用派生类的函数版本。

  • 动态绑定:在运行时,根据对象的实际类型(基类或派生类)来选择调用的函数版本。
  • 继承关系:重写仅发生在派生类中对基类的虚函数进行重新定义时。
  • 虚函数表:编译器为包含虚函数的类生成虚函数表,派生类会更新虚表中的函数指针指向自己的重写版本。

通过以上对比可以看到,重载和重写虽然允许使用相同的函数名,但它们的应用场景、实现机制和绑定方式都不同。

1.3.5 说说C语言如何实现C++语言中的重载

总结与对比

特性 C++ 中的重载(Overloading) C 语言实现重载的方式
实现机制 编译时通过**函数名修饰(Name Mangling)**实现 通过函数名变化、或函数指针手动实现
函数名 同名函数,编译器根据参数类型、数量选择不同的函数实现 需要人为改变函数名来模拟不同功能
编译器支持 编译器直接支持重载 C 语言不支持,需要手动编码实现重载效果
灵活性 自动根据参数选择函数 需要根据实际需求手动编写多个不同的函数或使用宏来处理不同的参数
参数差异处理 自动处理不同的参数类型和数量 通过编写多个函数或使用宏来处理不同的参数类型或数量

C 语言如何实现 C++ 的重载

由于 C 语言不支持函数重载,因此需要通过手动的方法来模拟重载的效果。以下几种方法常用于在 C 中实现类似于 C++ 中函数重载的功能:

1. 通过不同的函数名称

在 C 中,无法使用同样的函数名称,但可以通过人为修改函数名,模拟重载的效果。每个函数可以有不同的参数类型或数量,只需要给这些函数不同的名字即可。

示例:
int add_int(int a, int b) {
    return a + b;
}

double add_double(double a, double b) {
    return a + b;
}

通过这种方式,可以实现对不同参数类型进行操作,但函数名需要人为区分,比如add_intadd_double

2. 通过宏(Macros)

宏在 C 中可以用来简化代码并实现某种程度的函数重载效果。宏根据不同的参数类型或数量生成不同的函数调用。

示例:
#include <stdio.h>

#define add(x, y) _Generic((x), \
    int: add_int, \
    double: add_double \
)(x, y)

int add_int(int a, int b) {
    return a + b;
}

double add_double(double a, double b) {
    return a + b;
}

int main() {
    printf("%d\n", add(3, 4));        // 调用 add_int
    printf("%f\n", add(3.0, 4.0));    // 调用 add_double
    return 0;
}

通过_Generic宏,C 语言可以根据传入参数的类型选择不同的函数,实现类似重载的效果。

3. 通过函数指针(Function Pointers)

另一种实现函数重载的方式是通过函数指针,它允许在运行时根据具体情况调用不同的函数。这种方法可以提供一定程度的灵活性。

示例:
#include <stdio.h>

int add_int(int a, int b) {
    return a + b;
}

double add_double(double a, double b) {
    return a + b;
}

void add(void* a, void* b, char type) {
    if (type == 'i') {
        printf("%d\n", add_int(*(int*)a, *(int*)b));
    } else if (type == 'd') {
        printf("%f\n", add_double(*(double*)a, *(double*)b));
    }
}

int main() {
    int x = 3, y = 4;
    double m = 3.0, n = 4.0;

    add(&x, &y, 'i');      // 调用 add_int
    add(&m, &n, 'd');      // 调用 add_double

    return 0;
}

通过传递参数的指针和类型标识,可以在运行时根据需要选择不同的函数来执行,从而模拟函数重载。

总结

C 语言不直接支持函数重载,但可以通过改变函数名使用宏、或函数指针来模拟 C++ 中的重载功能。这种实现方式虽然灵活,但代码编写复杂度和维护成本较高,因为程序员需要手动处理不同的函数调用逻辑。

1.3.6 说说构造函数有几种,分别什么作用?

总结与对比

构造函数类型 作用 示例
默认构造函数 没有参数,初始化对象为默认状态。 ClassName();
有参构造函数 允许通过参数初始化对象的成员变量。 ClassName(int x);
拷贝构造函数 使用已有对象初始化新对象,进行深拷贝浅拷贝 ClassName(const ClassName& obj);
移动构造函数 用于从临时对象移动资源而不是复制资源,提升性能。 ClassName(ClassName&& obj);
委托构造函数 允许一个构造函数调用另一个构造函数,减少代码重复。 ClassName() : ClassName(10) {}
析构函数 在对象销毁时调用,释放资源,清理内存。 ~ClassName();

1. 默认构造函数

默认构造函数是没有参数的构造函数,它在对象创建时自动调用,用于将对象的成员变量初始化为默认值。如果用户没有显式定义任何构造函数,编译器会自动生成一个默认构造函数。

作用:
  • 初始化对象为默认状态。
  • 如果类中没有定义任何构造函数,编译器会生成一个隐式的默认构造函数。
示例:
class MyClass {
public:
    MyClass() {
        // 默认构造函数
        cout << "Default constructor called" << endl;
    }
};

2. 有参构造函数

有参构造函数允许通过传递参数来初始化对象的成员变量。用户可以根据需要定义多个有参构造函数来处理不同的初始化场景(这也是函数重载的一部分)。

作用:
  • 允许在创建对象时通过参数初始化成员变量。
  • 可以提供多个有参构造函数,支持不同的初始化需求。
示例:
class MyClass {
public:
    int x;
    MyClass(int a) : x(a) {
        cout << "Parameterized constructor called" << endl;
    }
};

3. 拷贝构造函数

拷贝构造函数用于通过另一个对象来初始化新对象。其主要作用是在对象需要复制时,比如通过值传递或返回对象时,进行深拷贝浅拷贝。编译器会自动生成一个默认的浅拷贝拷贝构造函数,但用户可以自定义以实现深拷贝。

作用:
  • 创建对象时,通过另一个对象的内容初始化新对象。
  • 处理指针成员的深拷贝,避免共享同一内存地址。
示例:
class MyClass {
public:
    int* ptr;
    MyClass(const MyClass& obj) {
        // 深拷贝示例
        ptr = new int(*obj.ptr);
        cout << "Copy constructor called" << endl;
    }
};

4. 移动构造函数

移动构造函数用于从临时对象(比如右值引用)中移动资源,而不是复制资源。这种方式避免了不必要的深拷贝,提高了性能。C++11 引入了移动构造函数以支持资源的高效转移。

作用:
  • 从另一个对象移动资源,而不是复制资源。
  • 提升性能,特别是在涉及大量数据或内存分配时。
示例:
class MyClass {
public:
    int* ptr;
    MyClass(MyClass&& obj) noexcept {
        // 移动构造函数,移动资源而非复制
        ptr = obj.ptr;
        obj.ptr = nullptr;
        cout << "Move constructor called" << endl;
    }
};

5. 委托构造函数

委托构造函数允许一个构造函数调用另一个构造函数,以减少代码重复并简化代码逻辑。这在复杂的初始化流程中非常有用。

作用:
  • 简化代码,避免重复编写初始化逻辑。
  • 通过调用其他构造函数来共享初始化代码。
示例:
class MyClass {
public:
    int x;
    MyClass() : MyClass(10) {
        // 调用有参构造函数
        cout << "Delegating constructor called" << endl;
    }

    MyClass(int a) : x(a) {
        cout << "Parameterized constructor called" << endl;
    }
};

6. 析构函数

虽然析构函数不是构造函数,但它与对象的生命周期密切相关。析构函数在对象销毁时自动调用,用于释放资源和清理动态分配的内存。它通常用于类中涉及到动态内存管理的场景。

作用:
  • 在对象生命周期结束时清理资源(例如,释放动态分配的内存、关闭文件等)。
  • 析构函数不能被重载。
示例:
class MyClass {
public:
    ~MyClass() {
        // 析构函数
        cout << "Destructor called" << endl;
    }
};

总结

C++中的构造函数类型多样化,针对不同的初始化需求提供了灵活的选择。默认构造函数有参构造函数用于基本的对象初始化,拷贝构造函数移动构造函数则处理对象的复制和移动,委托构造函数简化了初始化流程,最后析构函数负责对象销毁时的资源清理。

1.3.7 只定义析构函数,会自动生成哪些构造函数

总结与对比

定义析构函数的情况 自动生成的构造函数 说明
只定义析构函数 自动生成默认构造函数、拷贝构造函数、拷贝赋值运算符、移动构造函数和移动赋值运算符(在满足条件时) 编译器会根据需要生成默认的构造和赋值函数,除非用户定义了其他构造函数

自动生成的构造函数

如果在 C++ 类中只定义了析构函数,编译器会自动生成以下几种构造函数:

1. 默认构造函数

编译器会生成一个隐式的默认构造函数,如果类没有手动定义任何构造函数。默认构造函数会将对象的成员变量初始化为默认值或未定义状态。

说明:
  • 只有当没有定义其他构造函数时,编译器才会生成默认构造函数。
  • 该函数是无参的,自动初始化类的成员。
示例:
class MyClass {
public:
    ~MyClass() {
        // 析构函数定义
    }
};
// 自动生成的默认构造函数:MyClass() {}

2. 拷贝构造函数

编译器会生成一个拷贝构造函数,用于通过已有对象来初始化新对象。这个拷贝构造函数执行浅拷贝,即逐成员复制对象中的数据。

说明:
  • 如果类没有定义自定义的拷贝构造函数,编译器会生成一个隐式的拷贝构造函数。
  • 自动生成的拷贝构造函数进行逐位复制(浅拷贝)。
示例:
class MyClass {
public:
    ~MyClass() {
        // 析构函数定义
    }
};
// 自动生成的拷贝构造函数:MyClass(const MyClass& other) {}

3. 拷贝赋值运算符

编译器还会自动生成一个拷贝赋值运算符,用于将一个对象赋值给另一个对象。它和拷贝构造函数类似,也执行浅拷贝。

说明:
  • 如果没有手动定义拷贝赋值运算符,编译器会自动生成。
  • 自动生成的版本会逐位复制对象的成员变量。
示例:
class MyClass {
public:
    ~MyClass() {
        // 析构函数定义
    }
};
// 自动生成的拷贝赋值运算符:MyClass& operator=(const MyClass& other) {}

4. 移动构造函数(条件生成)

如果类中包含支持移动的资源(例如指针、动态内存等),编译器在满足某些条件时会自动生成移动构造函数。移动构造函数允许对象的资源从临时对象(右值)中移动,而不进行复制操作。

说明:
  • 如果类没有自定义拷贝构造函数或拷贝赋值运算符,且没有特殊操作阻止移动语义,编译器可能自动生成移动构造函数。
  • 如果定义了拷贝构造函数,编译器不会自动生成移动构造函数,除非显示要求。
示例:
class MyClass {
public:
    ~MyClass() {
        // 析构函数定义
    }
};
// 如果没有定义拷贝构造函数,自动生成移动构造函数:MyClass(MyClass&& other) noexcept {}

5. 移动赋值运算符(条件生成)

类似于移动构造函数,编译器也会在特定条件下生成移动赋值运算符,用于通过右值引用赋值操作,将资源从一个对象移动到另一个对象。

说明:
  • 如果类没有自定义赋值运算符且满足移动条件,编译器会生成移动赋值运算符。
  • 定义了拷贝赋值运算符或拷贝构造函数时,移动赋值运算符不会被自动生成。
示例:
class MyClass {
public:
    ~MyClass() {
        // 析构函数定义
    }
};
// 如果没有定义赋值运算符,自动生成移动赋值运算符:MyClass& operator=(MyClass&& other) noexcept {}

总结

在只定义析构函数的情况下,编译器会自动生成默认构造函数拷贝构造函数拷贝赋值运算符,并在某些情况下生成移动构造函数移动赋值运算符

1.3.8 说说一个类,默认会生成哪些函数

总结与对比

默认生成的函数 作用 生成条件
默认构造函数 初始化对象为默认状态。 如果类中没有手动定义任何构造函数,编译器自动生成。
拷贝构造函数 通过现有对象创建新对象,执行浅拷贝操作。 如果没有手动定义拷贝构造函数,编译器自动生成。
拷贝赋值运算符 将一个对象的内容复制给另一个对象,执行浅拷贝 如果没有手动定义拷贝赋值运算符,编译器自动生成。
析构函数 在对象销毁时清理资源,释放内存等。 如果没有手动定义析构函数,编译器自动生成。
移动构造函数 移动对象的资源而不是复制资源,用于右值引用,提升性能。 如果没有手动定义拷贝构造函数或赋值运算符,且编译器认为移动构造函数适用。
移动赋值运算符 移动右值引用对象的资源,而不是进行复制操作,提升性能。 如果没有手动定义拷贝赋值运算符或拷贝构造函数,且满足移动语义条件。

1. 默认构造函数

如果类中没有手动定义任何构造函数,编译器会自动生成一个默认构造函数。该构造函数用于将对象初始化为默认状态,例如将成员变量设置为未定义的默认值或零值。

作用:
  • 用于创建对象时进行默认的初始化操作。
  • 如果类中定义了其他构造函数(如有参构造函数),则不会生成默认构造函数。
示例:
class MyClass {
    int x;
};
// 自动生成的默认构造函数:MyClass() {}

2. 拷贝构造函数

编译器会生成一个拷贝构造函数,用于通过已有对象初始化新对象。拷贝构造函数执行浅拷贝,即逐个成员变量进行复制。如果类中没有定义自定义的拷贝构造函数,编译器会提供一个默认的版本。

作用:
  • 通过已有对象创建新对象,执行成员的逐位复制操作。
  • 如果类中定义了指针或动态内存管理,用户通常需要手动定义拷贝构造函数以执行深拷贝。
示例:
class MyClass {
    int x;
};
// 自动生成的拷贝构造函数:MyClass(const MyClass& other) {}

3. 拷贝赋值运算符

拷贝赋值运算符用于将一个对象的内容复制给另一个已经存在的对象。编译器生成的默认版本也执行浅拷贝操作,逐成员复制源对象的值。

作用:
  • 在赋值操作中,将一个对象的内容复制给另一个对象。
  • 如果类有动态内存管理,通常需要自定义以避免内存泄漏或共享。
示例:
class MyClass {
    int x;
};
// 自动生成的拷贝赋值运算符:MyClass& operator=(const MyClass& other) {}

4. 析构函数

析构函数用于在对象销毁时执行资源清理操作,例如释放动态分配的内存、关闭文件等。如果用户没有定义析构函数,编译器会自动生成一个默认的析构函数,该析构函数不会做任何特别的操作,除非类中包含需要手动管理的资源。

作用:
  • 在对象生命周期结束时清理资源。
  • 自动生成的析构函数是空的,不处理动态内存等复杂资源。
示例:
class MyClass {
    int x;
};
// 自动生成的析构函数:~MyClass() {}

5. 移动构造函数

如果类中定义了指针或其他需要动态管理的资源,编译器在满足某些条件时会生成移动构造函数,以通过移动资源而不是复制资源来优化性能。该函数用于右值引用,特别是在临时对象的场景中提升效率。

作用:
  • 通过移动而不是复制资源,减少不必要的深拷贝,提高性能。
  • 编译器仅在适当的情况下自动生成移动构造函数。
示例:
class MyClass {
    int* ptr;
};
// 自动生成的移