Cpp-04-拷贝构造

拷贝构造函数

一般来说,编译器会提供一个默认的拷贝构造函数,即使我们定义了其他构造函数,默认的拷贝构造会将参数的非static成员逐个拷贝到正在创建的对象中。对于类类型的成员,会使用类的拷贝构造函数进行拷贝,内置类型的成员则直接拷贝。(行为像值的类就是深拷贝)

  • 使用=号定义变量。
  • 将一个对象作为实参传递给非引用类型的形参。
  • 从一个返回类型为非引用类型的函数返回一个对象。
  • 用{}列表初始化一个数组或一个聚合类的成员。
  • 某些类对他们分配的对象使用拷贝初始化,如使用容器或调用其insert或push成员时(使用emplace创建都是直接初始化)。

    class mystring{
    private:
      char *str;
      /* data */
    public:
      mystring() : str(nullptr){}
    
      mystring(const mystring &mystr) {
          if (&mystr == this) {
              return;
          }
          if (mystr.str == nullptr) {
              str = nullptr;
              return;
          }
    
          size_t len = strlen(mystr.str);
          str = new char[len + 1];
          strcpy(str, mystr.str);
      }
    
      mystring(const char *mystr) { // 注意:使用 const char* 而不是 char*
          if (mystr == nullptr) {
              str = nullptr;
              return;
          }
    
          size_t len = strlen(mystr);
          str = new char[len + 1];
          strcpy(str, mystr);
      }
      //重载=
      mystring &operator=(const mystring &mystr) {
          if (&mystr == this) {
              return *this;
          }
    
          // 释放旧内存
          delete[] str;
    
          if (mystr.str == nullptr) {
              str = nullptr;
              return *this;
          }
    
          size_t len = strlen(mystr.str);
          str = new char[len + 1];
          strcpy(str, mystr.str);
    
          return *this;
      }
      //默认析构函数不会回收指针指向的空间
      // ~mystring() = default;
      //一般来说,如果定义了析构函数,也需要实现拷贝构造和拷贝赋值,因为默认是浅拷贝,会一起析构掉。
      ~mystring(){
          delete[] str; // 使用 delete[] 来释放数组分配的内存
          str = nullptr;
      }
      //重载<<
      friend ostream &operator<<(ostream &os, mystring &mystr);
    }
    //完善友元函数
    ostream &operator<<(ostream &os, mystring &mystr){
      if(mystr.str == nullptr){
          os << "mystring_ data is null" << endl;
          return os;
      }
      os << "mystring_ data is " << mystr1.m_str << endl;
      return os;
    }
    void initBase(){
      //直接初始化
      auto mystr1 = mystring("hello world!");
      //直接初始化
      auto mystr2(mystr1);
      //默认初始化
      auto mystr3 = mystring();
      //拷贝初始化
      auto mystr4 = mystr3;
      //拷贝初始化
      auto mystr5 = "hello world!";
      //拷贝初始化
      auto mystr6 = mystring("hello world!");
      cout << mystr1 << mystr2 << mystr3 << endl;
    }

    单例模式

    #include <iostream>
    #include <memory>
    #include <mutex>
    
    class Singleton_ {
    public:
      // 删除拷贝构造函数和赋值操作符
      Singleton_(const Singleton_&) = delete;
      Singleton_& operator=(const Singleton_&) = delete;
    
      // 获取单例实例
      static std::shared_ptr<Singleton_> getinstance() {
          // 如果非空直接返回不加锁节省效率
          if (_inst == nullptr) {
              std::call_once(_init_flag, []() {
                  _inst = std::make_shared<Singleton_>();
              });
          }
          return _inst;
      }
    
      //等价于使用双重检查锁
      static std::shared_ptr<Singleton_> getinstance() {
          // 第一次检查:如果非空直接返回,避免不必要的锁开销
          if (_inst == nullptr) {
              std::lock_guard<std::mutex> lock(_mutex);
              // 第二次检查:确保在多线程环境下只有一个实例被创建
              if (_inst == nullptr) {
                  _inst = std::make_shared<Singleton_>();
              }
          }
          return _inst;
      }
    
    private:
      // 私有构造函数,确保只能通过 getinstance() 创建实例
      Singleton_() {}
    
      // 静态成员变量初始化
      static std::shared_ptr<Singleton_> _inst;
      static std::once_flag _init_flag;
      static std::mutex _mutex;
    };
    
    // 在类外部初始化静态成员变量
    std::shared_ptr<Singleton_> Singleton_::_inst = nullptr;
    std::once_flag Singleton_::_init_flag;
    
    int main() {
      auto instance1 = Singleton_::getinstance();
      auto instance2 = Singleton_::getinstance();
    
      if (instance1 == instance2) {
          std::cout << "Both instances are the same." << std::endl;
      } else {
          std::cout << "Instances are different." << std::endl;
      }
    
      return 0;
    }

简单的共享指针

class SharePtr{
private:
    string *pstr;
    std::atomic<size_t> *pcount;

public:
    SharePtr() : pstr(nullptr), pcount(new std::atomic<size_t>(0));
    explicit SharePtr(const string &str) : pstr(new string(str)), pcount(new std::atomic<size_t>(1)){}
    //拷贝构造函数,自我赋值检查是多余的
    SharePtr(const SharePtr &sptr) : pstr(sptr.pstr), pcount(sptr.pcount){
        if(pcount != nullptr){
            (*pcount)++;
        }
    }
    SharePtr(const SharePtr &&sptr) noexcept : pstr(sptr.pstr), pcount(sptr.pcount){
        sptr.pstr = nullptr;
        sptr.pcount = nullptr;
    }
    SharePtr &operator=(const SharePtr &sptr){
        if(this != &sptr){
            //减少引用计数
            if(pcount != nullptr && --(*pcount) == 0){
                delete pstr;
                delete pcount;
            }
            pstr = sptr.pstr;
            pcount = sptr.pcount;
            if(pcount != nullptr){
                (*pcount)++;
            }
        }
        return *this;
    }
    SharePtr &operator=(SharePtr &&sptr) noexcept{
        if(this != &sptr){
            //减少引用计数
            if(pcount != nullptr && --(*pcount) == 0){
                delete pstr;
                delete pcount;
            }
            pstr = sptr.pstr;
            pcount = sptr.pcount;
            sptr.pstr = nullptr;
            sptr.pcount = nullptr;
        }
        return *this;
    }
    ~SharePtr(){
        if (pcount != nullptr && --(*pcount) == 0) {
            delete pstr;
            delete pcount;
        }
    }
    size_t getUseCount() const {
        return pcount ? *pcount : 0;
    }
    //解引用
    string &operator*() const {
        return *pstr;
    }
    string *operator->() const {
        return pstr;
    }
    explicit operator bool() const {
        return pstr != nullptr;
    }
}

swap

using std::swap;
class HasPtr
{
public:
    // 默认构造函数
    HasPtr() : m_str(new string()), m_index(0) { ++_curnum; }
    // 构造函数,从字符串初始化
    explicit HasPtr(const string &str) : m_str(new string(str)), m_index(0) { ++_curnum; }
    // 析构函数
    ~HasPtr() {
        delete m_str;
        --_curnum;
    }
    // 拷贝构造函数
    HasPtr(const HasPtr &hp) : m_str(new string(*hp.m_str)), m_index(hp.m_index) { ++_curnum; }
    HasPtr &operator=(const HasPtr &hp){
        cout << "this is operator = " << endl;
        if (&hp != this){
            // 先删除旧的字符串,再创建新的
            delete m_str;
            m_str = new string(*hp.m_str);
            m_index = hp.m_index;
        }
        return *this;
    }
    friend ostream &operator<<(ostream &os, const HasPtr &);
    // 友元函数 swap
    friend void swap(HasPtr &ptr1, HasPtr &ptr2) noexcept {
        using std::swap;
        swap(ptr1.m_str, ptr2.m_str);
        swap(ptr1.m_index, ptr2.m_index);
    }

private:
    string *m_str;
    int m_index;
    static int _curnum;
};
// 实现友元函数 << 操作符
ostream &operator<<(ostream &os, const HasPtr &hp) {
    os << "String: " << *hp.m_str << ", Index: " << hp.m_index;
    return os;
}
//等价于这个,新版本的operator=参数为HasPtr类型,而不是引用类型,这样hptr是赋值运算符右侧变量的副本
//随着函数返回,那么形参也就会自动销毁了
HasPtr &HasPtr::operator=(HasPtr hptr)
{
    // hptr是一个局部变量
    //交换后hptr.m_str指向this.m_str原来指向的内存
    swap(*this, hptr);
    // return返回*this后,hptr自动被回收
    return *this;
}
最后修改于:2025年01月22日 09:18

添加新评论