C++构造和折构函数详解,超详细!

个人主页:PingdiGuo_guo

收录专栏:C++干货专栏

大家龙年好呀,今天我们来学习一下C++构造函数和折构函数。

文章目录

1.构造函数

1.1构造函数的概念

1.2构造函数的思想

1.3构造函数的特点

1.4构造函数的作用

1.5构造函数的操作

1.6构造函数的分类

[1.6.1默认构造函数(Default Constructor)](#1.6.1默认构造函数(Default Constructor))

[1.6.2带参数构造函数(Parameterized Constructor)](#1.6.2带参数构造函数(Parameterized Constructor))

[1.6.3拷贝构造函数(Copy Constructor)](#1.6.3拷贝构造函数(Copy Constructor))

[1.6.4移动构造函数(Move Constructor)](#1.6.4移动构造函数(Move Constructor))

[1.6.5委托构造函数(Delegating Constructor)](#1.6.5委托构造函数(Delegating Constructor))

[1.6.6虚拟构造函数(Virtual Constructor)](#1.6.6虚拟构造函数(Virtual Constructor))

1.7构造函数的练习

1.7.1题目

1.7.2步骤

1.7.3代码

2.折构函数

2.1折构函数的概念

2.2折构函数的思想

2.3折构函数的特点

2.4折构函数的作用

2.5折构函数的操作

2.6折构函数的分类

2.6.1默认析构函数

2.6.2有特定目的的析构函数

3.构造函数和折构函数的利弊

3.1利

3.2弊

4.总结


1.构造函数

1.1构造函数的概念

构造函数是C++中的特殊成员函数,用于初始化新创建的对象。当一个类的对象被声明并实例化时,构造函数自动被调用。它的主要作用是确保对象在其生命周期开始时就处于有效状态。

1.2构造函数的思想

构造函数的思想是在创建对象时,用于初始化对象的成员变量和执行其他必要的操作。构造函数在对象创建时自动调用,不需要手动调用。构造函数的主要目的是确保对象被正确地初始化,以便在使用对象时不会出现未定义的行为或错误。

1.3构造函数的特点

1.构造函数的名称与类名称相同,并且没有返回类型(包括void类型),包括默认构造函数和带参数的构造函数。

2.构造函数可以重载,即在同一个类中可以定义多个具有不同参数列表的构造函数。

3.构造函数可以带有参数,在创建对象时,可以根据传递的参数值来初始化对象的成员变量。

4.构造函数可以进行一些初始化操作,例如分配内存、调用其他对象的构造函数等。

5.构造函数可以被继承,子类可以调用父类的构造函数来初始化父类的成员变量。

1.4构造函数的作用

1.对象的初始化:构造函数用于初始化对象的成员变量,确保对象在创建时具有正确的初始状态。

2.分配和初始化成员变量:构造函数可以分配并初始化类的成员变量,以便在对象创建时设置默认值或特定的初始状态。

3.进行必要的前置操作:构造函数可以执行一些必要的前置操作,例如打开文件、连接数据库等。

4.为对象分配内存:构造函数负责为对象分配内存空间,这通常发生在通过 new 运算符创建对象时。

5.链接到其他对象或资源:构造函数可以将对象链接到其他对象或资源,以便对象能够使用这些外部资源。

6.初始化对象的状态:构造函数可以设置对象的初始状态,例如设置指针成员变量为 null、设置计数器为 0 等。

7.执行其他必要的操作:构造函数还可以执行其他与对象创建相关的必要操作,如申请资源、注册回调函数等。

8.为派生类提供基类的初始化:在派生类的构造函数中,可以通过调用基类的构造函数来初始化继承自基类的成员变量。

1.5构造函数的操作

  1. 定义类结构:
cpp 复制代码
class Book {
private:
char* title; // 私有成员变量,用于存储动态分配的标题字符串

public:
// 构造函数声明
Book(const char* t);
};
  1. 实现构造函数:

在类外部实现构造函数,它负责接收一个 C 风格字符串并将其复制到动态分配的内存中。

cpp 复制代码
Book::Book(const char* t) {
// 计算字符串长度(包括终止符 '\0')
size_t len = strlen(t) + 1;

// 动态分配内存
title = new char[len];

// 使用标准库函数strcpy复制字符串
strcpy(title, t);
}
  1. 构造函数操作:

当创建 Book 对象时,构造函数会被自动调用:

cpp 复制代码
int main() {
Book book("The Title of the Book"); // 这里调用了构造函数
// ...
return 0;
}

在上述代码中,当我们创建 book对象时,传入的字符串 "The Title of the Book"将被复制到通过 new分配的内存空间中。

1.6构造函数的分类

构造函数可以分为以下几种分类:

1.6.1默认构造函数(Default Constructor)

如果一个类没有定义任何构造函数,那么编译器会自动生成一个默认构造函数。默认构造函数没有任何参数,并且不进行任何初始化操作。

cpp 复制代码
class MyClass {
public:
    // 默认构造函数
    MyClass() {
        // 进行初始化操作
    }
};

int main() {
    // 创建一个对象,调用默认构造函数
    MyClass obj;
    return 0;
}

1.6.2带参数构造函数(Parameterized Constructor)

带参数构造函数接收一定数量和类型的参数,用于对对象的成员变量进行初始化。通过传递不同的参数值,可以创建具有不同初始状态的对象。

cpp 复制代码
class MyClass {
public:
    // 带参数构造函数
    MyClass(int value) {
        // 进行初始化操作,使用参数value对对象进行初始化
    }
};

int main() {
    // 创建一个对象,调用带参数构造函数
    MyClass obj(10);
    return 0;
}

1.6.3拷贝构造函数(Copy Constructor)

拷贝构造函数是一种特殊的构造函数,用于创建一个新对象,并将现有对象的值复制到新对象中。拷贝构造函数通常用于对象之间的值传递或对象的复制。

cpp 复制代码
class MyClass {
public:
    // 拷贝构造函数
    MyClass(const MyClass& other) {
        // 进行拷贝操作,将other对象的值复制到当前对象中
    }
};

int main() {
    // 创建一个对象,调用拷贝构造函数
    MyClass obj1;
    MyClass obj2 = obj1;  // 或者 MyClass obj2(obj1);
    return 0;
}

1.6.4移动构造函数(Move Constructor)

移动构造函数是C++11引入的一种特殊构造函数,用于将右值引用参数转移或"窃取"其资源,而不是进行复制。移动构造函数可以提高程序的效率和性能。

cpp 复制代码
class MyClass {
public:
    // 移动构造函数
    MyClass(MyClass&& other) {
        // 进行移动操作,将other对象的资源移动到当前对象中
    }
};

int main() {
    // 创建一个对象,调用移动构造函数
    MyClass obj1;
    MyClass obj2 = std::move(obj1);  // 或者 MyClass obj2(std::move(obj1));
    return 0;
}

1.6.5委托构造函数(Delegating Constructor)

委托构造函数是C++11引入的一种特殊构造函数,它允许一个构造函数调用同一个类的另一个构造函数,以避免代码冗余。

cpp 复制代码
class MyClass {
public:
    // 默认构造函数
    MyClass() : MyClass(0) {
        // 委托给带参数的构造函数
    }
    
    // 带参数构造函数
    MyClass(int value) {
        // 进行初始化操作,使用参数value对对象进行初始化
    }
};

int main() {
    // 创建一个对象,调用默认构造函数
    MyClass obj;
    return 0;
}

1.6.6虚拟构造函数(Virtual Constructor)

虚拟构造函数是一种在基类中声明的构造函数,用于在派生类中动态创建基类对象。虚拟构造函数是通过在基类中声明纯虚函数来实现的。

cpp 复制代码
class Base {
public:
    // 虚拟构造函数
    virtual Base* clone() const {
        return new Base(*this);
    }
    
    // 其他成员函数和数据成员
};

class Derived : public Base {
public:
    // 虚拟构造函数的重写
    virtual Derived* clone() const {
        return new Derived(*this);
    }
    
    // 其他成员函数和数据成员
};

int main() {
    // 创建一个派生类对象,调用虚拟构造函数的重写
    Base* basePtr = new Derived();
    Base* copy = basePtr->clone();
    
    // 其他操作
    
    delete basePtr;
    delete copy;
    return 0;
}

1.7构造函数的练习

1.7.1题目

设计并实现一个 Person类,其中包含姓名和年龄属性,并编写一个自定义构造函数来初始化这些属性。

1.7.2步骤

  1. 定义 `Person` 类结构,声明私有成员变量。

  2. 编写构造函数,接受姓名(字符串类型)和年龄(整型)作为参数。

  3. 在构造函数内,对传入的参数进行有效性检查(可选)。

  4. 将传入的参数值赋给对应的成员变量。

1.7.3代码

cpp 复制代码
// Step 1: 定义 Person 类
class Person {
private:
    std::string name;
    int age;

public:
    // Step 2: 编写构造函数
    Person(const std::string& n, int a) {
        // Step 3: 参数有效性检查(例如年龄应该大于0)
        if (a < 0) {
            throw std::invalid_argument("Age must be positive.");
        }

        // Step 4: 赋值给成员变量
        name = n;
        age = a;
    }

    // 其他成员函数...
};

// 使用构造函数创建一个 Person 对象
int main() {
    Person person("John Doe", 30);
    return 0;
}

2.折构函数

2.1折构函数的概念

析构函数是C++中另一种特殊的成员函数,它与构造函数相反,在对象生命周期结束时(例如对象的作用域结束或者delete一个动态分配的对象时)自动调用。析构函数主要用于清理工作,如释放内存、关闭文件等。

2.2折构函数的思想

析构函数是一种特殊的成员函数,在对象销毁时自动调用,用于释放对象所占用的资源。

析构函数与构造函数相对应,构造函数用于对象的初始化,而析构函数用于对象的清理与释放。

2.3折构函数的特点

1.析构函数没有参数,且没有返回值。

2.析构函数的名称与类名相同,前面加上波浪号(~)作为前缀。

3.只能有一个析构函数,且不能被继承或重载。

4.析构函数不能被显式地调用,不能被重载。

5.对象销毁时,析构函数会自动调用,从而完成对象的销毁和资源的释放。

6.析构函数按照对象的创建顺序的逆序调用,即先创建的对象后销毁。

2.4折构函数的作用

1.释放动态分配的内存或资源。

2.关闭打开的文件、数据库连接等。

3.清理对象中的临时数据和状态。

4.执行其他清理和释放操作。

2.5折构函数的操作

  1. 定义析构函数:

在类内部声明析构函数,它将在 Book 对象生命周期结束时(例如,超出作用域或显式销毁)自动调用。

cpp 复制代码
class Book {
// ...
public:
// 析构函数声明
~Book();
};
  1. 实现析构函数:

在类外部实现析构函数,它负责释放之前构造函数中动态分配的内存。

cpp 复制代码
Book::~Book() {
// 检查指针是否已经分配过内存
if (title != nullptr) {
delete[] title; // 释放内存
title = nullptr; // 设置为 nullptr 防止悬挂指针
}
}
  1. 析构函数操作:

析构函数的操作是自动的,不需要程序员显式调用。当 Book对象离开其作用域时,析构函数会自动执行。

cpp 复制代码
int main() {
{
Book book("The Title of the Book"); // 创建对象时构造函数被调用
} // 这里 book 作用域结束,析构函数将被自动调用,释放 title 的内存
return 0;
}

在上述代码中,当 book 对象超出作用域时,析构函数将被调用,释放之前为 title分配的内存。这样就确保了程序不会出现内存泄漏的问题。

2.6折构函数的分类

析构函数的分类主要分为默认析构函数和有特定目的的析构函数。

2.6.1默认析构函数

如果在类中没有显式定义析构函数,编译器会自动生成一个默认的析构函数。默认析构函数会自动释放对象占用的内存,但不执行任何其他操作。

cpp 复制代码
class MyClass {
public:
    // 构造函数
    MyClass() {
        // 构造函数的代码
    }
    // 默认析构函数
    ~MyClass() {
        // 自动生成的析构函数,无需显式定义
    }
};

2.6.2有特定目的的析构函数

有些情况下,需要在对象销毁时执行特定的清理操作,例如释放资源、关闭文件、释放动态分配的内存等。这时可以自定义析构函数,并在其中编写相应的清理代码。

cpp 复制代码
class MyClass {
private:
    int* ptr; // 动态分配的内存指针

public:
    // 构造函数
    MyClass() {
        ptr = new int[10]; // 动态分配内存
    }

    // 有特定目的的析构函数
    ~MyClass() {
        delete[] ptr; // 释放动态分配的内存
    }
};

在上述代码中,我们定义了一个具有特定目的的析构函数,用于释放动态分配的内存。当对象销毁时,析构函数会自动被调用,执行内存释放操作。

需要注意的是,每个类只能有一个析构函数,不能重载析构函数。析构函数没有参数,也没有返回值。析构函数的名称与类名称相同,并在前面加上波浪线(~)作为标识。

使用析构函数可以确保在对象生命周期结束时进行清理工作,避免内存泄漏和资源浪费的问题。同时,通过自定义析构函数,可以根据具体需求执行特定的清理操作。

3.构造函数和折构函数的利弊

3.1利

  • 构造函数确保了对象从一开始就处于已知的良好状态。

  • 析构函数提供了自动资源管理机制,减少内存泄漏和其他资源泄露的风险。

3.2弊

  • 如果构造函数或析构函数内部逻辑复杂,可能影响程序性能或增加错误的可能性。

  • 构造函数抛出异常会导致未初始化的对象,而析构函数如果在异常处理期间抛出异常,则可能导致程序行为不可预测。

4.总结

本篇博客到这里就结束了,感谢大家的支持与观看,如果有好的建议欢迎留言,谢谢大家啦!

相关推荐
_院长大人_1 小时前
设计模式-工厂模式
java·开发语言·设计模式
MATLAB代码顾问1 小时前
MATLAB实现决策树数值预测
开发语言·决策树·matlab
不染尘.2 小时前
2025_11_7_刷题
开发语言·c++·vscode·算法
似水এ᭄往昔3 小时前
【C++】--stack和queue
开发语言·c++
仰望—星空3 小时前
MiniEngine学习笔记 : CommandListManager
c++·windows·笔记·学习·cg·direct3d
csbysj20203 小时前
R 绘图 - 散点图
开发语言
会跑的兔子3 小时前
Android 16 Kotlin协程 第一部分
android·开发语言·kotlin
Js_cold4 小时前
Verilog函数function
开发语言·fpga开发·verilog
我是苏苏4 小时前
C#基础:如何从现有类库复制一个新的类库,并且加入解决方案
开发语言·c#
算法与编程之美4 小时前
理解Java finalize函数
java·开发语言·jvm·算法