C++实现智能指针(二)

一. 实现版本v2

  • 拷贝构造函数
  • 赋值操作符函数
拷贝与赋值对于指针而言意味共享其所指的内存资源,对应于智能指针的实现就是mPointer成员指向同一块内存资源。

默认情况下C++编译器会为我们定义的类生成拷贝构造函数和赋值操作符的实现,但是对于我们的智能指针而言,使用系统默认生成的赋值操作符会有问题。如下:

{  // 语句块
  SmartPointer<SomeClass> spclass1 = new SomeClass; //1
  SmartPointer<SomeClass> spclass2 = new SomeClass; //2
  spclass2 = spclass1;      //3 调用 赋值操作符
}

那么当该语句块执行完毕,我们的期望是,代码1和2处new出来的对象应该都要有智能指针自动去释放,但是如果使用系统默认为我们生成的赋值操作符的实现,2处new出来的对象将永远不会被释放,而1处new出来的对象会被释放两次。

实现智能指针头文件 smartpointer.h

/* 
* file name : smartpointer.h
* desp : 智能指针版本v2
*/
#ifndef __SMARTPOINTER_H__
#define __SMARTPOINTER_H__

template <typename T>  // 将智能指针类定义成模板类
class SmartPointer {
public:
    // 默认构造函数
    SmartPointer():mPointer(NULL) {std::cout <<"Create null smart pointer."<< std::endl;}    
    // 接收不同指针类型的构造函数
    SmartPointer(T *p):mPointer(p) {std::cout <<"Create smart pointer at "<<static_cast<const void*>(p)<<std::endl;}     
    // 析构函数
    ~SmartPointer(){
        std::cout << "Release smart pointer at "<<static_cast<const void*>(mPointer)<<std::endl;
        // 实现内存资源自动销毁机制
        if (mPointer) delete mPointer;
    }
    // 拷贝构造函数
    SmartPointer(const SmartPointer &other):mPointer(other.mPointer) {
        std::cout <<"Copy smart pointer at "<<static_cast<const void*>(other.mPointer)<<std::endl;
    }     
   // 赋值操作符                
   SmartPointer &operator = (const SmartPointer &other) {
        // 处理自我赋值的情况
        if (this == &other) return *this;
        // 处理底层指针的释放问题
        if (mPointer) delete mPointer;
        mPointer = other.mPointer;  
        std::cout <<"Assign smart pointer at "<<static_cast<const void*>(other.mPointer)<<std::endl;
        return *this;
   } 

private:
    T *mPointer; // 指向智能指针实际对应的内存资源,根据参数自动推导规则,定义内部资源指针类型
};
#endif // __SMARTPOINTER_H__

测试代码 sptestcase2.cpp

/* 
* file name : sptestcase2.cpp
* desp : 智能指针测试代码 case2 测试智能指针的拷贝与赋值
*/

#include <iostream>
#include "smartpointer.h"
class SomeClass{
public:
    SomeClass(){std::cout << "SomeClass default constructor !"<<std::endl;}
    ~SomeClass(){std::cout << "SomeClass deconstructor !"<<std::endl;}
};

void testcase2(void)
{
    // 先创建一个智能指针,再给已创建好的智能指针赋值
    SmartPointer<SomeClass> spclass1 = new SomeClass;
    std::cout << std::endl;
    // 自我赋值
    spclass1 = spclass1;
    std::cout << std::endl;

    // 创建另一个智能指针,与先前的指针指向同一块内存资源
    SmartPointer<SomeClass> spclassother = spclass1;
    std::cout << std::endl;

    // 先创建智能指针,再用另一个智能指针给已创建好的智能指针赋值
    SmartPointer<SomeClass> spclass2 = new SomeClass;
    std::cout << std::endl;
    spclass2 = spclass1;
    std::cout << std::endl;
}

int main(void)
{
    testcase2();
    return 0;
}

结果分析


  • v2版本不足
虽然我们通过实现赋值运算符与拷贝构造函数的使得多智能指针间在共享底层资源时能够避免对象资源泄漏的现象,但是在释放智能指针时还是存在一些问题:就是当有多个智能指针执行同一块底层资源,在释放时,每个指针都会去释放一次底层资源,这就造成了最后的double free错误。


二. 知识点查漏补缺

//拷贝构造函数,对象不存在,用别的对象对其初始化
A a;
A b=a;

//赋值函数,对象存在,用别的对象给其赋值
A a;
A b;
b=a;
  • 赋值操作符的实现需要解决两个问题:
  1. 底层指针释放:该问题就是造成我们需要自行实现赋值操作符的原因,我们的解决方案是在给底层指针赋新值前,先将原来的释放
  2. 自我赋值:会导致我们针对问题1的解决方案出现问题。自我赋值时mPointer和other.mPointer会指向同一块内存对象,因此按照上述针对问题1的解决方案,最终我们的智能指针将会指向一块被释放了的内存。采用经典解决方式。
//底层指针的释放
if (mPointer) delete mPointer;  
mPointer = other.mPointer; 
//自我赋值的情况
SmartPointer<SomeClass> spclass = new SomeClass;
spclass = spclass;
//解决自我赋值问题的经典方式
if (this == &other)
   return *this;
  • 头文件与源文件

头文件:防止重复预编译。多个cpp文件引用该头文件,不会发生错误

#ifndef HEAD_H
#define HEAD_H
...
#endif
源文件:多个cpp文件分别编译生成obj文件后链接。

猜你喜欢

转载自blog.csdn.net/qq_32274259/article/details/80199364