0

这部分问题提供了背景信息,可以忽略

我正在开发一个模板库,该库严重依赖于奇怪重复的模板模式的使用。类结构背后的想法是用户可以

1)。使用带有标准方法的预定义类。这些类是基类的非常简单的叶子,仅提供构造函数/析构函数,声明变量成员并将基类声明为友元。对派生类的变量成员进行操作的所有方法都在基类中定义。

2)。使用基类创建他/她自己的扩展。这种方法还允许用户引入他们自己的方法来操作相同的变量成员。

设计仅强制执行单级继承。

我的问题主要是关于第 2 条。在当前的实现中,用户必须隐式定义所有构造函数(即描述类的动态变量成员的内存分配的完整过程等)。

问题

下面的示例演示了对使用 CRTP 在基类构造函数中提供派生类的堆变量的内存分配定义的可能性的调查。

基类的一部分

template<class TLeafType, class MyClass> class sysBaseDiscreteTrajectoryPoint {
 ...

//one of the base constructors
sysBaseDiscreteTrajectoryPoint(const MyClass& MyClassInstance) {
    std::cout << "Base additional constructor called" << std::endl;
    std::cout << asLeaf().Point << std::endl;
    asLeaf().Point=new MyClass(MyClassInstance);
    std::cout << asLeaf().Point << std::endl;
}

TLeafType& asLeaf(void) {
return static_cast<TLeafType&>(*this);
}

...
};

派生类:

template<class MyClass> 
class sysDiscreteTrajectoryPoint: public sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass> {
...
friend class sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass>;
private:
    MyClass* Point;
public:
    sysDiscreteTrajectoryPoint(const MyClass& MyClassInstance): sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass>(MyClassInstance){
        std::cout << "Derived additional constructor called " << std::endl; 
        std::cout << Point << std::endl;
        std::cout << *Point << std::endl;
    }
...
}

主要的:

int a(5);
sysDiscreteTrajectoryPoint<int> A(a);

该代码产生以下输出:

Base additional constructor called
0x847ff4
0x8737008
Derived additional constructor called 
0x8737008
5
Derived destructor called 
Base destructor called 

输出表明该概念可能是可行的。但是,我有两个问题。

1)。我想确保我了解代码执行期间发生的所有过程。特别是,我对流程的效率感兴趣,因为我可能需要从上面介绍的类中实例化大量对象,并且我想了解会发生什么Point(是否有任何隐藏的重新定义?)

2)。这个问题与使用库boost为派生类的成员定义智能指针有关。当我尝试用 替换原始指针时boost::shared_ptr,在尝试通过基类为派生类的成员分配内存时收到分段错误错误。代码的重要部分如下所示。

基类的一部分:

template<class TLeafType, class MyClass> class sysBaseDiscreteTrajectoryPoint {
 ...

//one of the base constructors
sysBaseDiscreteTrajectoryPoint(const MyClass& MyClassInstance) {
    std::cout << "Base additional constructor called" << std::endl;
    std::cout << asLeaf().Point << std::endl;
    asLeaf().Point.reset(new MyClass(MyClassInstance));
    std::cout << asLeaf().Point << std::endl;
}

TLeafType& asLeaf(void) {
return static_cast<TLeafType&>(*this);
}

...
};

部分派生类:

template<class MyClass> 
class sysDiscreteTrajectoryPoint: public sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass> {
...
friend class sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass>;
private:
    boost::shared_ptr<MyClass> Point;
public:
    sysDiscreteTrajectoryPoint(const MyClass& MyClassInstance): sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass>(MyClassInstance){
        std::cout << "Derived additional constructor called " << std::endl; 
        std::cout << Point << std::endl;
        std::cout << *Point << std::endl;
    }
...
}

主要的:

int a(5);
sysDiscreteTrajectoryPoint<int> A(a);

该代码产生以下输出:

Base additional constructor called
0x28d324
Segmentation fault

我也试过了scoped_ptr。但是,它在运行时失败,但出现了不同的错误:

Base additional constructor called
*** glibc detected *** ./TestSystem: free(): invalid pointer: 0x00d3fff4 ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x6b961)[0xc4e961]
...

我假设它与boost智能指针的操作细节有关。有谁知道如何解决这个问题?

4

2 回答 2

1

由于上述答案中给出的原因,在编译时的地址是已知的,但它本身仍未初始化,因为尚未调用派生类构造函数,因此没有机会隐式调用其实例成员'构造函数,包括. 因此,当您调用assign 时,它首先尝试在其包含的任何虚假地址处释放(并可能删除)该对象(以避免泄漏现有的引用),然后再分配和引用新对象。我相信,第一步就是导致段错误的原因。shared_ptrshared_ptrshared_ptrreset()shared_ptr

如果shared_ptr构造函数首先运行,它将使其包含的原始指针为空,从而防止后续reset()调用尝试释放虚假地址处的对象。

使用asLeaf()从基类构造函数访问派生类对于非 POD 类型本质上是不安全的,因为构造不完整(派生类的成员尚未构造)。顺便说一下,这就是为什么从基构造函数调用虚方法永远不会从更多派生类调用覆盖的原因——该语言明确防止在整个对象的构造完成之前调用覆盖,因为在大多数情况下,整个对象的状态是尚未定义。

可能有更好的解决方案适合您,但一种可行的方法是从基类的构造函数中删除该初始化代码,并将其放入init()派生类的每个实例化时显式调用的函数中。init()仍然可以存在于基类中,但它更安全,因为在它运行时所有内容都已初始化。

旁注:避免shared_ptr无故放入小物件。在这种情况下,您可能有合理的需求,但总的来说,我更喜欢将成员直接聚合到单所有者指针和单所有者指针到共享指针,因为开销会增加。单所有者指针涉及堆分配,共享指针还增加了计数/跟踪所有者的成本,以便在无法访问时删除对象。

于 2012-07-31T07:30:32.040 回答
0

您怎么可能Point从基构造函数访问属于派生类的成员?调用基构造函数时,派生类部分不存在。也许它只是“偶然”起作用。

但它肯定会失败shared_ptr,因为您尝试在它有机会初始化之前分配它。

于 2012-07-21T19:47:03.530 回答