仅标题库循环依赖项 我试图在外部C API周围创建一个头文件C++库。 C API使用void*指针作为句柄。 我的想法是: // resource.hpp class Resource { public: // RAII constructor, destructor, etc. // ... void do_something(const Handle & h) { do_something_impl( (void *) h); } }; // handle.hpp class Handle { public: Handle(size_t n, const Resource & res) : p_(res.allocate(n)), res_(res) {} // cast operation operator void *() const { return p_; } private: void * p_; Resource & res_; };
这里的问题是:(a)句柄必须保留对资源的引用,(b)资源需要能够将句柄强制转换为void*。不幸的是,这会导致循环依赖 有没有关于如何重组的想法 注意:答案不是简单地“include xxx.hpp”或向前声明其中一个类。 这需要以某种方式进行重组,我只是不太明白如何重组 将仅标题库循环依赖项 我试图在外部C API周围创建一个头文件C++库。 C API使用void*指针作为句柄。 我的想法是: // resource.hpp class Resource { public: // RAII constructor, destructor, etc. // ... void do_something(const Handle & h) { do_something_impl( (void *) h); } }; // handle.hpp class Handle { public: Handle(size_t n, const Resource & res) : p_(res.allocate(n)), res_(res) {} // cast operation operator void *() const { return p_; } private: void * p_; Resource & res_; };,c++,circular-dependency,raii,C++,Circular Dependency,Raii,这里的问题是:(a)句柄必须保留对资源的引用,(b)资源需要能够将句柄强制转换为void*。不幸的是,这会导致循环依赖 有没有关于如何重组的想法 注意:答案不是简单地“include xxx.hpp”或向前声明其中一个类。 这需要以某种方式进行重组,我只是不太明白如何重组 将类句柄作为前向声明添加到资源文件顶部是不起作用的,因为(void*)强制转换是句柄定义的一部分,资源仍然无法看到。同样,将强制转换更改为void*ptr()成员函数也会导致相同的问题 将函数定义移动到.cpp文件也不是一个答
类句柄
作为前向声明添加到资源文件顶部是不起作用的,因为(void*)
强制转换是句柄定义的一部分,资源仍然无法看到。同样,将强制转换更改为void*ptr()
成员函数也会导致相同的问题
将函数定义移动到.cpp文件也不是一个答案——它只需要是头文件。不要将头文件包含到彼此中,而是对类进行前向声明。这样,它们可以用作引用或指针 因此,在resource.hpp中:
class Handle;
class Resource {
public:
// RAII constructor, destructor, etc.
// ...
void do_something(const Handle & h) {
do_something_impl( (void *) h);
}
};
在handle.hpp中:
class Resource;
class Handle
{
public:
Handle(size_t n, const Resource & res)
: p_(res.allocate(n)), res_(res) {}
// cast operation
operator void *() const { return p_; }
private:
void * p_;
Resource & res_;
};
由于在
do\u something
函数中使用了void*
typecasting操作符,因此必须将该实现移动到单独的源文件中。在该源文件中,您可以同时包含两个头文件,因此可以访问所有函数。不要将头文件包含在彼此中,而是对类进行正向声明。这样,它们可以用作引用或指针
因此,在resource.hpp中:
class Handle;
class Resource {
public:
// RAII constructor, destructor, etc.
// ...
void do_something(const Handle & h) {
do_something_impl( (void *) h);
}
};
在handle.hpp中:
class Resource;
class Handle
{
public:
Handle(size_t n, const Resource & res)
: p_(res.allocate(n)), res_(res) {}
// cast operation
operator void *() const { return p_; }
private:
void * p_;
Resource & res_;
};
由于在
do\u something
函数中使用了void*
typecasting操作符,因此必须将该实现移动到单独的源文件中。在该源文件中,您可以同时包含两个头文件,因此可以访问所有函数。好吧,这是一个很好的解决方案(再次!):
//resource.hpp
班级资源;
模板类句柄;
类资源{
公众:
//RAII构造函数、析构函数等。
// ...
void do_某事(const Handle&h){
做一些简单的事((void*)h);
}
};
//handle.hpp
模板
类句柄{
公众:
句柄(大小、常量、资源和资源)
:p_(res.allocate(n)),res_(res){}
//铸造操作
运算符void*()常量{return p_;}
私人:
void*p;
TResource&res;
};
好吧,这是救援的模板(再一次!):
//resource.hpp
班级资源;
模板类句柄;
类资源{
公众:
//RAII构造函数、析构函数等。
// ...
void do_某事(const Handle&h){
做一些简单的事((void*)h);
}
};
//handle.hpp
模板
类句柄{
公众:
句柄(大小、常量、资源和资源)
:p_(res.allocate(n)),res_(res){}
//铸造操作
运算符void*()常量{return p_;}
私人:
void*p;
TResource&res;
};
在使用转发声明的基础上,确保使用标题保护。@Joe:转发声明在这里不起作用(请参见编辑)。在使用转发声明的基础上,确保使用标题保护。@Joe:转发声明在这里不起作用(请参见编辑)。@Joachim--在vs2010中,这给出了“无法从'const Handle&h'转换为'void*';源或目标的类型不完整。换句话说,Resource无法看到操作符void*()
。另外,将类资源添加到handle.hpp将不起作用,因为handle需要看到Resource.allocate()成员函数@DavidH然后您必须在handle.hpp
中包含resource.hpp
文件。只要你不做相反的事情就好了。至于错误,这是因为您试图将非指针类型转换为指针类型。@Joachim--转换运算符的整个要点就是将句柄类型转换为指针。你能建议一个不同的方法吗?void*ptr()成员函数会给出相同的错误并导致相同的问题。@DavidH难道不能使用运算符的地址来实际获取一个真正的指针吗?@Joachim——在vs2010中,这会给出“无法从'const Handle&h'转换为'void*';源或目标的类型不完整。换句话说,Resource无法看到操作符void*()
。另外,将类资源添加到handle.hpp将不起作用,因为handle需要看到Resource.allocate()成员函数@DavidH然后您必须在handle.hpp
中包含resource.hpp
文件。只要你不做相反的事情就好了。至于错误,这是因为您试图将非指针类型转换为指针类型。@Joachim--转换运算符的整个要点就是将句柄类型转换为指针。你能建议一个不同的方法吗?void*ptr()成员函数将给出相同的错误并导致相同的问题。@DavidH难道不能使用运算符的地址来实际获取一个真正的指针吗?