使用unique_ptr的多态类的C++static_cast和dynamic_cast

C++ static_cast and dynamic_cast of polymorphic classes using unique_ptr

本文关键字:cast C++static dynamic 多态 unique ptr 使用      更新时间:2023-10-16

我在C++中的多态类上练习static_cast和dynamic_cast。我尝试了使用原始指针和unique_ptr。前者不会制造问题,后者会制造问题。在这里我展示我的代码:-

#include <iostream>
#include <memory>
#include <exception>
#include <stdexcept>
using namespace std;
class A
{
    int a, id=0;
    static int i;
public:
    A()
    {
        id=++i;
        cout<<"constructing A: "<<id<<"n";
    }
    virtual void get()
    {
        cout<<"enter a: ";
        cin>>a;
    }
    virtual void disp()
    {
        cout<<"a = "<<a<<"n";
    }
    virtual ~A()
    {
        cout<<"destroying A: "<<id<<"n";
    }
};
int A::i=0;
class B: public A
{
    int b;
public:
    B()
    {
        cout<<"constructing Bn";
    }
    void get()
    {
        cout<<"enter b: ";
        cin>>b;
    }
    void disp()
    {
        cout<<"b = "<<b<<"n";
    }
    ~B()
    {
        cout<<"destroying Bn";
    }
};
void show (unique_ptr<B> &p)
{
    p->get();
    p->disp();
}
void d_cast (unique_ptr<A> &pa)
{
    unique_ptr<B> pb;
    try
    {
        pb.reset(dynamic_cast<B*>(pa.release()));
        if (pb==nullptr)
            throw runtime_error {"nullptr exception"};
        show(pb);
        cout<<"dynamic_cast successfulnn";
    }
    catch (exception &e)
    {
        cout<<"dynamic_cast unsuccessful: "<<e.what()<<"nn";
    }
    pa.reset(pb.release());
}
void s_cast (unique_ptr<A> &pa)
{
    unique_ptr<B> pb;
    try
    {
        pb.reset(static_cast<B*>(pa.release()));
        if (pb==nullptr)
            throw runtime_error {"nullptr exception"};
        show(pb);
        cout<<"static_cast successfulnn";
    }
    catch (exception &e)
    {
        cout<<"static_cast unsuccessful: "<<e.what()<<"nn";
    }
    pa.reset(pb.release());
}
int main()
{
    cout<<R"(using "unique_ptr<A> pa with new A" :-)"<<"nn";
    unique_ptr<A> pa(new A);    // (1)
    d_cast(pa);
    s_cast(pa);      // (2)
    cout<<"n"<<R"(using "unique_ptr<A> pa with new B" :-)"<<"nn";
    pa.reset(new B);
    d_cast(pa);
    s_cast(pa);
    return 0;
}

代码的输出为:-

using "unique_ptr<A> pa with new A" :-
constructing A: 1
dynamic_cast unsuccessful: nullptr exception
static_cast unsuccessful: nullptr exception

using "unique_ptr<A> pa with new B" :-
constructing A: 2
constructing B
enter b: 7
b = 7
dynamic_cast successful
enter b: 8
b = 8
static_cast successful
destroying B
destroying A: 2

我只有两个问题,因为我已经标记:-

  1. 为什么第一个对象{用(1)表示}没有被破坏,而用"新B"调用的对象却被破坏了?

  2. 为什么(2)抛出异常?有趣的是,如果我反转s_cast(pa)d_cast(pa)的位置,那么(2)不会抛出任何异常,并且工作正常(然而,问题(1)仍然存在)。

好!因此,您需要更改函数d_cast函数定义,如下所示:-

void d_cast (unique_ptr<A> &pa)
{
unique_ptr<B> pb;
A *aptr=pa.release();   // make a pointer of type A
try
{
    pb.reset(dynamic_cast<B*>(aptr));   // assign aptr instead of pa.release() here
    if (pb==nullptr)
    throw runtime_error {"nullptr exception"};
    show(pb);
    cout<<"dynamic_cast successfulnn";
    pa.reset(pb.release());   // reset pa with pb.release() and not with aptr becomes pb has the ownership of aptr
}
catch (exception &e)
{
    cout<<"dynamic_cast unsuccessful: "<<e.what()<<"nn";
    pa.reset(aptr);    // reset aptr back to pa as pb holds no ownership of aptr
}
}

正如您所知,d_cast将失败,因此dynamic_cast<B*>(pointer_of_type_A)的表达式将返回nullptr。如果存在引用而不是指针,则会引发std::bad_cast异常。但是,因为您使用的是release()函数,所以unique_ptr对象pa取消了指针的所有权,并且没有对象或指针可以追溯它。因此,您应该使用A *aptr来保持释放的指针,并在强制转换失败时将其返回到pa

如果你这样做,你的两个问题都解决了