Consider the following code:
/*
* myclass.h
*/
class myclass_impl
{
// ...
}
boost::shared_ptr<myclass_impl> myclass;
我能否某种方式使
myclass_impl
(或者至少是它的直接使用)对定义它的翻译单元私有化,从而允许客户端只使用myclass
类型定义?我试图实现的目标是让编译器在某人直接使用实现类时警告我。在源文件中声明你的类(而非头文件),这样其他编译单元就无法访问它。然后,在头文件中使用前向声明来声明一个指针/引用。
或者定义一个实现头文件,并注释说明它不应该被包含在其他源文件中。
/*
* myclass.h
*/
class myclass_impl;
class myclass
{
boost::shared_ptr<myclass_impl> myclass_i;
public:
myclass() : myclass_i(new myclass_impl) { }
int getI() const;
};
/*
* myclass.cpp
*/
class myclass_impl
{
int i;
public:
myclass_impl() : i(4) { }
int getI() const { return i; }
};
int myclass::getI() const
{
return myclass_i->getI();
}
void *
类型,但我认为这是一个不太好的方向。更好的方法是使用一些完全抽象的类,只包含虚析构函数。 - ony您可以创建接口myclass
并使用工厂方法提供在同一文件中定义的匿名命名空间中的私有类myclass_impl
的实例。另一种变体是Pimpl。
文件myclass.h
class myclass
{
public:
virtual void doSomething() = 0;
static boost::shared_ptr<myclass> createInstance();
};
文件 myclass_impl.cpp
#include "myclass.h"
namespace {
class myclass_impl : public myclass
{
public:
virtual void doSomething() { std::cerr << "Hi there" << std::endl; }
};
}
static boost::shared_ptr<myclass> myclass::createInstance()
{
return new myclass_impl();
}
更新(新增可怕的解决方案):
myclass.h
class destroyable { virtual ~destroyable() {} };
class myclass {
private:
boost::shared_ptr<destroyable> pimpl;
public:
void doSomething();
};
myclass_impl.cpp
namespace {
class myclass_impl : public destroyable {
public:
void doSomething() { /* ... */ }
};
}
void myclass::doSomething() { static_pointer_cast<myclass>(pimpl)->doSomething(); }
myclass_impl
有私有构造函数,并且只能由一个单独的友元工厂类创建。然而,我需要使工厂类公开可用。您能详细说明一下您的答案吗? - Dan NestorcreateInstance()
?我希望通过更简单的语法myclass_object->method()
来访问myclass_impl
的成员。 - Dan Nestormyclass f()
或 void f(myclass x)
的地方产生影响,我想。但你仍然能够使用 void f(myclass &x)
和 myclass *f()
。实际上,我认为你需要隐藏实现细节(即避免在头文件中显示 private
部分)。看起来我不太理解你想要实现的目标。你需要什么样的细节呢? - ony免责声明,本答案中使用的public和private是泛指,并不按照标准中的定义,而是表示其他翻译单元可以访问或使用的意思。根据标准,private
用于表示公共成员。
这完全取决于类的具体定义以及需要向其他翻译单元发布多少内容。
如果你只在.cpp文件中声明和定义该类(我还会使用一个未命名的命名空间来避免名称冲突),那么该类型将无法从该翻译单元之外访问。
如果在任何地方引用了该类,并且必须将其发布到该翻译单元之外(存在一个公共类型的成员,该成员是指向公共类的指针/引用),那么最好的方法就是在头文件中提供一个前向声明(现在不再是未命名的命名空间,可能作为使用它的类的private
类型内部)。
作为最后的手段,您可以在头文件中提供类型的整个定义(公共类型直接持有该类型的成员),但仍然可以将该类型作为私有内部类型保留在公共类型中,禁止在该类型之外使用它(包括友元)。
正如您所看到的,私有和公共的含义以及可以控制的内容是不同的。通过在头文件中不提供定义并使用未命名的命名空间,您使其对其他TU不可访问;通过仅提供前向声明,该类型已知存在,但不能在需要完整类型的任何上下文中使用(函数仍然可以接受和转发指向该类型的指针)。在另一个层面上,通过将其设置为内部类型并将其设置为不同类型的private
,定义将被知道,但它将无法在封闭类型的friend
之外使用...
不清楚你想要实现什么。但是已经有人问过你了,而你并没有很好地澄清。在你的回答评论中,你写道:“我的意思不仅仅是禁止实例化,还包括任何对该类的引用(例如在函数中使用它作为参数类型)”。
从字面上理解,这意味着在你的头文件中使用以下内容:
struct BlahImpl;
typedef boost::shared_ptr<BlahImpl> Blah;
// Functions that create or give access to Blah instances.
客户端代码可以创建或访问Blah
实例,并复制它们(具有隐含的共享语义),但实际上不能对它们执行任何操作。最好的情况是,它们可以作为某个函数之前已调用(生成实例)的证明。或者也许某些东西由涉及这些实例的函数调用模式控制,但无论如何,boost::shared_ptr
将完全无关紧要和多余。
所以也许你并不是确切地意思是你写的那样,而是更像是“任何BlahImpl
实例都应该是动态分配的,并由boost::shared_ptr
封装”。
如果是这样,你可以按照以下方式实现:
您可以通过使析构函数非public
(最好是protected
),并提供某些销毁实例的方法(最简单的方法是将friend
授予一个常见的销毁函数模板),来强制执行动态分配。
您可以通过多种方式确保给定智能指针的包装。主要问题是转发构造函数参数。一种与C++98兼容的方法是通过宏进行转发,在这种情况下,“您不能无意中创建除此宏之外的实例”可以通过混淆来实现,即混淆new
表达式。
示例:
#include <boost/shared_ptr.hpp>
#include <iostream>
#include <stddef.h> // ptrdiff_t, size_t
#include <string>
using namespace std;
namespace cpp11 {
using boost::shared_ptr;
};
template< class Type >
void destroy( Type const* p ) { delete p; }
class OnlySharedPtrUsage
{
public:
virtual ~OnlySharedPtrUsage() {}
struct InstantiationObfuscation;
static void* operator new( size_t size, InstantiationObfuscation* )
{
return ::operator new( size );
}
static void operator delete( void* p, InstantiationObfuscation* )
{
::operator delete( p );
}
static void operator delete( void* p )
{
::operator delete( p );
}
};
#define NEW_SHARED( type, args ) \
::cpp11::shared_ptr<type>( \
new( (type::InstantiationObfuscation*)0 ) type args, \
destroy<type> \
)
class MyClass
: public OnlySharedPtrUsage // The NEW_SHARED macro simplies.
{
template< class Type > friend void destroy( Type const* );
private:
string helloText_;
MyClass( MyClass const& ); // No such.
MyClass& operator=( MyClass const& ); // No such.
protected:
virtual ~MyClass() // Only dynamic allocation allowed.
{
cout << "MyClass::<destroy>()" << endl;
}
public:
string helloText() const { return helloText_; }
MyClass( string const& text )
: helloText_( text )
{
cout << "MyClass::<init>( string )" << endl;
}
};
int main()
{
// MyClass o( "a" ); // ! Does not compile, not dynamic.
// MyClass* p = new MyClass( "b" ); // ! Does not compile, not "mangled".
cpp11::shared_ptr< MyClass > sp = NEW_SHARED( MyClass,( "Hello from MyClass!" ) );
cout << sp->helloText() << endl;
}
make_shared
的优化。混淆的分配器函数(正式为放置函数)与make_shared
不太匹配。但我想可以通过定义分配器类并使用alloc_shared
来完成。嗯...
class myclass
{
private:
class myclass_impl
{
public:
void do_something() { std::cerr << "Hello there" << std::endl; }
};
public:
typedef boost::shared_ptr<myclass_impl> ptr_type;
static ptr_type construct()
{ return ptr_type(new myclass_impl()); }
};
int main()
{
myclass::myclass_impl *x; // error: 'class myclass::myclass_impl' is private
myclass::ptr_type::element_type *y; // ok
myclass::ptr_type x = myclass::construct();
x->do_something(); /// Hello there
}
这是你想要的吗?
顺便提一下,无法隐藏 myclass_impl
,因为 boost::shared_ptr<T>
提供了访问底层类型的方式。
myclass::value_type
)。长回答:您所说的“使用”是什么意思?通过使某些构造函数和析构函数不可用,可以禁止某些用法。 - Kerrek SB