智能指针和dynamic_cast

13

如果这个问题已经有答案了,我提前道歉,因为我找不到答案。

注意:这是一项作业任务,如果你觉得不舒服回答,我完全理解。

我有以下内容:

ptr.h:

template<typename T>
class Ptr {
    T* address;
    size_t* counter;
    Ptr(T* address) : address(address), counter(new size_t(1)) { }
    Ptr(const Ptr& other) : address(other.address), counter(other.counter) 
    { 
        ++(*counter); 
    }
    virtual ~Ptr() { 
        if (0 == --(*counter)) { delete address; delete counter; } 
    }
    Ptr& operator=(const Ptr& right) {
        if (address != right.address) {
            if (0 == --(*counter)) { delete address; delete counter; }
            address = right.address;
            counter = right.counter;
            ++(*counter);
        }
        return *this;
    }
    T& operator*()  const { TRACE(address); return *address; }
    T* operator->() const { TRACE(address); return address;  }
    T* raw()        const { TRACE(addr); return addr;  }
};

main.cc:

#include <iostream>
#include "ptr.h"

using std::cout;
using std::endl;


class Base {
public:
    Base()  { std::cout << "Base()" << std::endl; }
    virtual ~Base() { std::cout << "~Base()" << std::endl; }
    virtual std::string str() { return "In Base::str()"; }
};

class Derived: public Base {
public:
    Derived()  { std::cout << "Derived()" << std::endl; }
    ~Derived() { std::cout << "~Derived()" << std::endl; }
    std::string str() { return "In Derived::str()"; }
};



int main() {
Ptr<Base> base(new Base());
Ptr<Derived> derived(new Derived());

Ptr<Base> pbase(0);
Ptr<Derived> pderived(0);

    // upcasting can be done like this, but is it the best way?
    pbase = *((Ptr<Base>*)(&derived));
    cout << pbase->str() << endl;   // outputs: "In Derived::str()"

    /* 
     * downcasting should be done using dynamic_casts
     * but how can I downcast here? 
     * what do I know so far:
     * 1. just because Derived is a subclass of Base does not mean Ptr<Derived> is a 
     * subclass of Ptr<Base> so there is no hierarchy between the two so I cannot 
     * really use dynamic_casts here
     * 2. The dynamic_cast I do use is sort of useless no? since pbase is a Ptr<Base>
     */
    pderived = *((Ptr<Derived>*)(dynamic_cast<Ptr<Base>*>(&pbase)));
    cout << pderived->str() << endl;


return 0;
}

现在的目标是使用dynamic_cast进行前后转换,虽然我找到了许多关于智能指针的有趣细节,但没有真正解释这是如何实现的。
我尝试获取pbase的地址字段,然后用该地址初始化一个新的Ptr,但我的引用计数全部出错了。
我尝试创建一个新的Ptr,其中包含对pderived计数器的引用,但我无法设置pderived的地址字段,所以我也被卡住了。
我告诉你们这些信息,因为:1.我想说明在在线寻求帮助之前,我已经花了相当长的时间来解决这个问题;2.我希望你们知道我已经尝试过什么。
我真的需要一些建议。只是如何获取:
pderived = <SOMETHINGSOMETHING>pbase<SOMETHINGSOMETHING>

谢谢!


3
如果这是一道作业题,不用担心,只要记得打上作业标签。人们会提供指导性的回答而不是直接给你答案。 - chris
如果您想要更详细的解释,这是一篇不错的阅读材料:http://meta.stackexchange.com/questions/10811/how-to-ask-and-answer-homework-questions - chris
谢谢提供链接 :) 我之前不知道还有作业标签。 - mlnyc
1个回答

7
通常智能指针类会暴露一个动态转换的包装器,以正确处理底层智能指针对象,例如,C++0x有dynamic_pointer_cast函数。请注意,您的*((Ptr<Derived>*)(dynamic_cast<Ptr<Base>*>(&pbase)));可能会出现问题,特别是如果您有多重继承,因为它不会调整address处的内部指针到子类中所需的正确偏移量,以获取所需的任何超类。
在实现级别上,引用计数智能指针类通常会在每个智能指针中携带两个指针;一个直接指向对象(适当转换为指针类型),另一个指向一个包含引用计数、原始对象指针和删除例程指针的结构。这允许任何转换的指针正确地获取原始对象并调用其析构函数。目前,您正在直接指向size_t,这不会给您提供这些信息。
无论如何,一旦您具备了析构函数的这种间接方式,当到达转换这些智能指针的时候,您只能转换外部指针,留下这个内部引用计数结构,在销毁时间到来时保持不变。
当然,将所有这些转化为代码留给读者作为练习 :)

"以及一个指向删除例程的指针" - 或者直接是一个删除函数对象。 - Xeo
是的,那是另一个选项。或者你可以有一个基本的refcount结构,带有一个纯虚拟删除例程,以及实现删除例程和任何可能需要的状态的模板子类。等等。 - bdonlan
1
这实际上是我所知道的所有实现都使用的方法。STL在GoingNative 2012上对shared_ptr的工作原理进行了很好的讲解 - Xeo
谢谢你指引我正确的方向 :) 我很快就会掌握这个。非常好的答案! - mlnyc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接