目录

C++构造函数

构造函数

  • 构造函数是一种特殊的成员函数,它在对象创建时自动调用,用于初始化对象的成员变量。

C++会为每个类提供一个默认构造函数,用户也可以自己定义一些带参数的构造函数。

cpp 复制代码
class MyClass
{
public:
    MyClass(int val) : x(val) {};
private:
    int x;
};

构造函数可以是虚函数吗?

  • 构造函数不能是虚函数!
  • 虚函数的机制依赖于虚函数表,而虚表对象的建立需要在调用构造函数之后才能完成。构造函数是用来初始化对象的,而在对象的初始化阶段虚表对象还没有被建立,如果构造函数是虚函数会导致对象初始化和多态机制的矛盾。

浅拷贝和深拷贝?

浅拷贝只是简单地复制对象的值,而不复制对象锁拥有的资源或内存。 深拷贝不仅复制对象的值,还会新分配内存并复制对象所拥有的资源。

  • 拷贝构造函数和赋值运算符重载可以用来实现深拷贝!

拷贝构造函数

  • 当创建一个新对象时,用另一个对象初始化它。
cpp 复制代码
#include <cstring>

class MyClass
{
private:
    char* data;
public:
    MyClass(const char* inputData)
    {
        data = new char[std::strlen(inputData) + 1];
        std::strcpy(data, inputData);
    }

    // 拷贝构造函数 -> 深拷贝
    MyClass(const MyClass& other)
    {
        data = new char[std::strlen(other.data) + 1];
        std::strcpy(data, other.data);
    }

    ~MyClass()
    {
        delete[] data;
    }
};

赋值构造函数

  • 对一个已经存在的对象进行赋值操作。
cpp 复制代码
class MyClass
{
// ...
public:
    MyClass& operator=(const MyClass& other)
    {
        // 防止自我赋值
        if (this != &other)
        {
            delete[] data;
            data = new char[std::strlen(other.data) + 1];
            std::strcpy(data, other.data);
        }
        return *this;
    }
};

深拷贝示例

cpp 复制代码
#include <cstring>
#include <iostream>

class MyClass
{
private:
    char* data;
public:
    MyClass(const char* inputData)
    {
        std::cout << "MyClass(const char* inputData)" << std::endl;
        data = new char[std::strlen(inputData) + 1];
        std::strcpy(data, inputData);
    }

    // 拷贝构造函数 -> 深拷贝
    MyClass(const MyClass& other)
    {
        std::cout << "MyClass(const MyClass& other)" << std::endl;
        data = new char[std::strlen(other.data) + 1];
        std::strcpy(data, other.data);
    }

    // 赋值构造函数 -> 深拷贝
    MyClass& operator=(const MyClass& other)
    {
        std::cout << "MyClass& operator=(const MyClass& other)" << std::endl;
        // 防止自我赋值
        if (this != &other)
        {
            delete[] data;
            data = new char[std::strlen(other.data) + 1];
            std::strcpy(data, other.data);
        }
        return *this;
    }

    ~MyClass()
    {
        std::cout << "~MyClass()" << std::endl;
        delete[] data;
    }
};

int main()
{
    MyClass obj1("Hello");

    MyClass obj2 = obj1; // obj2是新对象 -> 拷贝构造函数
    
    MyClass obj3("World");

    obj3 = obj2; // obj2给obj3赋值 -> 赋值构造函数

    return 0;
}

/*
    MyClass(const char* inputData)
    MyClass(const MyClass& other)
    MyClass(const char* inputData)
    MyClass& operator=(const MyClass& other)
    ~MyClass()
    ~MyClass()
    ~MyClass()
*/

移动构造和移动赋值运算符

移动构造函数和移动赋值运算符主要搭配移动语义来提高性能,特别是在需要转移大对象资源的时候,具体场景包括:

  1. 当函数返回一个对象时,用移动构造函数可以避免返回值拷贝;
  2. 当函数传递参数时,使用右值 + 移动构造函数可以避免参数拷贝。
  3. 当需要将一个大对象从一个容器移动到另一个容器时,用移动赋值运算符可以避免重复的资源分配和释放!

移动构造函数和移动赋值运算符的调用过程:通过std::move或右值引用转移资源所有权;将原对象置于有效但未指定的状态,确保其析构函数能够安全地调用!

右值引用和std::move

右值引用:&&符号表示,在函数声明中,若参数是右值引用,那么它表示这个参数可以接受右值而非左值,即只能绑定到临时对象或是要被销毁的对象。

例如:void foo(std::string&& str);函数foo只能接受右值std::string

std::move:将一个左值显示转换成右值引用,即可顺利调用移动构造函数或移动赋值运算符。

cpp 复制代码
std::string s1 = "Hello"; // s1是左值
std::string s2 = std::move(s1); // s1被move转换为右值引用,s2可以调用std::string的移动构造函数,将资源从s1转移到s2

使用示例

  • 高效的资源转移!
cpp 复制代码
#include <cstring>
#include <iostream>

class MyClass
{
private:
    int* ptr;
public:
    // 构造函数
    MyClass(int value)
    {
        std::cout << "MyClass(int value)" << std::endl;
        ptr = new int(value);
    }

    // 拷贝构造函数
    MyClass(const MyClass& other)
    {
        std::cout << "MyClass(const MyClass& other)" << std::endl;
        ptr = new int(*other.ptr); // 深拷贝
    }

    // 赋值构造函数
    MyClass& operator=(const MyClass& other)
    {
        std::cout << "MyClass& operator=(const MyClass& other)" << std::endl;
        if (this != &other)
        {
            delete ptr; // 释放当前对象的内存
            ptr = new int(*other.ptr); // 深拷贝
        }
        return *this; // 解引用
    }

    // 移动构造函数
    // noexcept声明函数不抛出异常,可以提高效率!
    MyClass(MyClass&& other) noexcept
    {
        std::cout << "MyClass(MyClass&& other)" << std::endl;
        // 转移资源
        this->ptr = other.ptr;
        other.ptr = nullptr; // 确保other处于有效状态,但未指定
    }

    MyClass& operator=(MyClass&& other) noexcept
    {
        std::cout << "MyClass& operator=(MyClass&& other)" << std::endl;
        if (this != &other)
        {
            delete this->ptr;
            this->ptr = other.ptr;
            other.ptr = nullptr; // 确保other处于有效状态,但未指定
        }
        return *this;
    }

    ~MyClass()
    {
        std::cout << "~MyClass()" << std::endl;
        delete ptr;
    }

    void printValue()
    {
        std::cout << *ptr << std::endl;
    }
};

int main()
{
    MyClass obj1(13);

    MyClass obj2 = std::move(obj1); // 调用移动拷贝构造函数

    MyClass obj3(23);

    // obj1.printValue(); // error:obj1资源已被转移
    obj2.printValue();
    obj3.printValue();

    obj3 = std::move(obj2); // 调用移动赋值构造函数

    // obj2.printValue(); // error:obj2资源已被转移
    obj3.printValue();

    return 0;
}

/*
    MyClass(int value)
    MyClass(MyClass&& other)
    MyClass(int value)
    13
    23
    MyClass& operator=(MyClass&& other)
    13
    ~MyClass()
    ~MyClass()
    ~MyClass()
*/

注意事项

std::move()只有一个作用:无论输入参数是左值还是右值,都利用static_cast强制转化成右值。

  • 右值可以触发移动构造函数或者移动赋值函数,一般默认原对象不再继续使用,以高效移动减少内存占用!
  • 如果原对象仍需继续使用,亦可以在移动构造函数或移动赋值函数保留原对象!

析构函数

析构函数主要用于释放资源,防止内存泄露。

析构函数要声明为虚函数吗?

如果一个类可能被继承,并且需要在删除指向派生类对象的基类指针时确保正确调用派生类的析构函数,那么基类的析构函数必须是虚函数,否则,可能导致资源泄露!

虚函数的机制:当一个类的成员函数声明为虚函数时,C++会为该类生成一个虚函数表,存储指向虚函数的指针。 在运行时,基于当前对象的实际类型,虚函数表指针用于动态绑定,调用正确的函数版本。

cpp 复制代码
#include <cstring>
#include <iostream>

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 *obj = new Derived();
    delete obj;

    return 0;
}
  • 如果基类的析构函数不是虚函数,当通过基类指针删除派生类对象时,只会调用基类的析构函数,而不会调用派生类的析构函数!
本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
笺上山河梦17 分钟前
C++ 的 输入输出流(I/O Streams)
开发语言·c++·学习·算法
oioihoii32 分钟前
软考软件设计师30天备考指南
c++
说码解字42 分钟前
C++ 完美转发
c++
DK2215144 分钟前
C++ STL 容器简介(蓝桥杯适用精简版)
开发语言·c++·蓝桥杯
敲上瘾1 小时前
基于UDP协议的群聊服务器开发(C/C++)
linux·服务器·c++·网络协议·udp·信息与通信
jieyucx1 小时前
C++中的引用:深入理解与实用示例
java·jvm·c++
Zfox_2 小时前
Git 远程操作全攻略:从基础到实战
linux·运维·c++·git·elasticsearch
半青年2 小时前
物联网MQTT协议原理与技术解析
java·c++·python·物联网·网络协议·5g
愚润求学3 小时前
【专题刷题】滑动窗口(四):
c++·笔记·leetcode·刷题
拾忆-eleven3 小时前
C++ 算法(12):数组参数传递详解,值传递、指针传递与引用传递
数据结构·c++·算法