c++类中实例化对象的正确方法

文章讲述了如何在C++中正确管理类对象的生命周期,避免局部作用域导致的对象销毁。通过std::shared_ptr确保对象与ClassA保持一致的生命周期,并介绍了使用impl_ptr封装功能,以支持无参数构造和等号赋值操作,方法源于ROS1源码中subscriber和publisher的设计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如果你想在类中实例化一个对象,而且随着这个类一直存在,就必须考虑这个对象的生命周期,避免超出局部作用域就被销毁了。常见错误:

ClassB.hpp:

class ClassB
{
public:
    ClassB(int a)
    {
    }
}

想在ClassA中用ClassB对象:

#include "ClassB.hpp"

class ClassA
{
public:
    ClassA(int a)
    {
        ClassB obj(a);
    }
}

这里obj实例化放在了ClassA的构造函数里,其实ClassA构造完后,obj就被销毁了!

想要obj生命周期和ClassA一样长,就需要把obj作为ClassA的一个成员变量:

#include "ClassB.hpp"

class ClassA
{
public:
    ClassA(int a)
    {
    }
private:
    ClassB obj(1);
}

但这又带来了新问题,我们希望obj的构造(初始化)是在ClassA的构造函数内进行,而不是在变量定义时就构造。这对于构造函数有参数的obj尤其重要。有两种解决方案:

一、用std::shared_ptr来实例化对象

当然你也可以直接用原始的指针,但是指针的销毁需要手动处理,所以我们这里用std::shared_ptr智能指针,其对象的析构是随着退出作用域而自动进行的。

#include "ClassB.hpp"
#include <memory>  // for std::shared_ptr


class ClassA
{
public:
    ClassA(int a)
    {
        obj_ptr = std::make_shared_ptr<ClassB>(a);
    }
private:
    std::shared_ptr<ClassB> obj_ptr;
}

用std::make_shared_ptr就可以实例化对象了,由于obj_ptr是ClassA的成员变量,所以生成的obj对象也是跟ClassA生命周期一样,直到ClassA被销毁才会析构。

这里不用std::unique_ptr是因为unique_ptr很严格只能在一个地方使用,不能赋值(=)只能转移(move),所以很多地方用到obj_ptr等号赋值的时候会报错。

二、用impl_ptr来赋值和拷贝构造

如果你封装的ClassB不想用起来还得单独用shared_ptr,而是想和int、float这些基础变量一样用如下的方法分开定义和赋值:

#include "ClassB.hpp"

class ClassA
{
public:
    ClassA(int a)
    {
        obj = ClassB(a); //带参数构造,并赋值给obj
    }
private:
    ClassB obj; //没有带参数构造
}

那么你可以用impl_ptr(impl是implemente的缩写)方法,重新定义构造、拷贝构造、和赋值函数,本质也是用shared_ptr,只不过把shared_ptr用impl_ptr提前封装在内部了:

ClassB.hpp:

#include <memory> //for std::shared_ptr

class ClassB
{
private:
    class Impl
    {
         public:
            Impl(int a) {}
            void func() {}   
    }
    std::shared_ptr<Impl> impl_ptr_;

public:
    ClassB() {} // 无参数的构造函数

    ClassB(int a) // 有参数的构造函数
    {
        impl_ptr_ = std::make_shared<Impl>(a);
    }

    /* 重写的拷贝构造函数 */
    ClassB(const ClassB& rhs)
    {
        impl_ptr_ = rhs.impl_ptr_; // 拷贝的其实是内部的shared_ptr指针
    }

    /* 重载赋值函数为拷贝构造,default就是拷贝构造 */
    ClassB& operator=(const ClassB& other) = default;  

    void func()
    {
        impl_ptr_->func();
    }
}

可以看到,这里解决了几个问题:

1、通过同时编写无参数和有参数的构造函数,让定义ClassB obj没有参数也行。

2、用impl_ptr封装了ClassB的所有功能,ClassB只是一个露在外面的空壳,实际功能都是impl_ptr实现的。

3、重载拷贝构造函数为impl_ptr的复制,并将赋值函数(=)重载为拷贝构造函数,实现了用等号就可以移动赋值 obj = ClassB(a)。一般等号赋值是有问题的,因为普通的赋值函数只会拷贝类里一些基础的类型变量,一些自定义的方法和成员变量并不会拷贝过去。通过拷贝内部的shared_ptr巧妙地解决了这个问题。

这是我在阅读ROS1源码时学到的方法,ROS1的subscriber、publisher、timer都是这种构造方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值