可以安全地使用静态强制转换,为每个实例使用唯一的虚拟 int type() 来提高性能



我有一个密集的多态对象层次结构,每个对象都有一个强制性的virtual int type() = 0方法,该方法是为每个对象手动编写的。

使用

dynamic_cast 对树进行"安全"处理会产生很大的开销,并且由于我已经有一个虚拟方法,它将为每个不同类型的返回一个唯一的整数,我认为使用不安全的静态强制转换是安全的。但是我可能会忽略一些东西,例如在多重继承的情况下或其他可能导致错位的事情,这可能会导致静态强制转换"崩溃"?

层次结构由不同的上下文在

基类级别遍历,每个上下文都有解释数据的自定义方法,因此对于每个对象,在发生强制转换的位置switch (type)和"根据类型执行"。

type() 虚在"最底层类"中声明,该类在继承中始终排在第一位。

根据标准 5.2.9/2,您打算执行的静态强制转换是安全的,前提是基类不是虚拟基类,也不是虚拟基类的基。

这里提供了标准中提供的示例:

struct B { };
struct D : public B { };
D d;
B &br = d;
static_cast<D&>(br); // produces lvalue to the original d object
// Note that this works because B is not a virtual base class

因此,除非您对虚拟基数使用多重继承,否则没关系。

但是,如果您有以下想法,请小心:

struct B { };
struct D1 : public virtual B { };
struct D2 : public virtual B {}; 
struct E : D1, D2 {};  
E e; 
B *pb=&e;   //
static_cast<E*>(pb);   // ouch !!! This would not safe 
D1 *pd1 = &e; 
static_cast<E*>(pd1);   // but this would be ok 
只要

确保转换确实在做正确的事情,就可以在代码中用static_cast替换dynamic_castdynamic_cast涉及一些运行时开销,而这些开销是static_cast 中缺少的。

但是,我不确定在多个/虚拟基类的情况下是否会有一些替换的含义。我遇到过这样一种情况,即我存储了operator new返回的地址,然后在后来检查时显示该地址不存在。

//Get the address from new
void* operator new(size_t size);
//Store this address in list.
std::list<void*> rawAddresses;
//Function to check if address is present
isAddressPresent(void* address)         <<< This vomitted error when I passed object of
                                        <<< class which has virtual base.

以这种方式使用 static_cast 应该没问题——只要你能保证你投射到正确的类型。我强烈建议一个简单的单元测试,它可以验证你没有任何type()冲突(例如,复制粘贴错误可能导致SubclassB具有与SubclassA相同的type() id) - 只需将所有类型添加到地图并检查冲突。此外,您可以安全地创建帮助程序函数,以保证正确的类型(取决于您的使用模式)。下面是一个示例。

// g++ test.cpp --std=c++11
#include <iostream>
#include <cassert>
#include <stdexcept>
class Base {
public:
    virtual int type() = 0;
};
class SubClassA : public Base {
public:
    virtual int type() override { return 1; }
};
class SubClassB : public Base {
public:
    virtual int type() override { return 2; }
};
class BaseConversion {
public:
    BaseConversion(Base *ptr) : _ptr(ptr) {}
    Base *_ptr;
    template<typename T>
    operator T() const {
        throw std::runtime_error("invalid Base * type()");
    }
};
template<>
BaseConversion::operator SubClassA*() const {
    if ( _ptr->type() != 1 ) {
        throw std::runtime_error("type() not SubClassA");
    }
    return static_cast<SubClassA*>(_ptr);
}
template<>
BaseConversion::operator SubClassB*() const {
    if ( _ptr->type() != 2 ) {
        throw std::runtime_error("type() not SubClassB");
    }
    return static_cast<SubClassB*>(_ptr);
}
int main(int argc, char **argv) {
    Base *ptr = new SubClassA();
    try {
        SubClassA *a = BaseConversion(ptr);
    } catch (const std::exception &e) {
        std::cout << e.what() << std::endl;
    }
    try {
        SubClassB *b = BaseConversion(ptr);
    } catch (const std::exception &e) {
        std::cout << e.what() << std::endl;
    }
    return 0;
}

这确实意味着您需要为每个子类专门化一个模板Base。但是,这也意味着您可以为每个单元编写一个单元测试,以执行转换并帮助确保您始终检查正确的type() id 并转换为正确的子类(只需在指针声明/分配中写入一次SubClassA,而不是在static_cast中)。

如果您执行以下操作,这将对您(性能方面)没有帮助:

switch (ptr->type()) {
case 1:
    SubClassA *a = BaseConversion(ptr);
    /* do stuff with a */
    break;
case 2:
    SubClassB *b = BaseConversion(ptr);
    /* do stuff with b */
    break;
}

。因为您实际上会检查 type() 两次。但是,只要type()只返回一个整数,而不必进行计算来确定它是什么类型,那么如果您这样做,性能就不会很糟糕(您可以做更糟糕的事情)。请注意,由于static_cast,这确实有效地消除了给定指针的任何const性!可能有办法解决这个问题,但我会把它留给你一个进一步的练习,因为我很懒。

相关内容

最新更新