一个班级可以私有翻译单位吗?

时间:2011-11-25 13:21:57

标签: c++ typedef private

请考虑以下代码:

/*
 * myclass.h
 */

class myclass_impl
{
    // ...
}

boost::shared_ptr<myclass_impl> myclass;

我能以某种方式使myclass_impl(或至少直接使用它)对其定义的翻译单元是私有的,允许客户只使用myclass typedef吗?我想要实现的是让编译器在有人直接使用实现类时提醒我。

5 个答案:

答案 0 :(得分:3)

在源文件(不是头文件)中声明您的类,并且无法从其他翻译单元访问它。然后,使用forward声明在头文件中声明指针/引用。

或者定义一个impl头文件并注释它不应该包含在其他源文件中。

/*
 * 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(); 
}

答案 1 :(得分:3)

您可以创建接口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(); }

答案 2 :(得分:1)

(免责声明,在此答案中使用 public private 是通用的,而不是根据标准中的定义,而是意味着可访问< / em>或可用由其他翻译单位。private用于表示公共成员按照标准)

这完全取决于班级的特定定义,以及需要向其他翻译单位发布多少。

如果仅在.cpp文件中声明和定义类(我也会使用未命名的命名空间来避免名称冲突),那么该类型将无法从该翻译单元外部访问。

如果这个类被引用到必须在外部发布到这个翻译单元的任何地方(有一个公共类型的成员,它是一个公共类的指针/引用),那么下一个最好的事情就是提供一个标头中的前向声明(现在在未命名的命名空间之外,可能是使用它的类内部的private类型)。

作为最后的手段,您可以在标题中提供类型的完整定义(公共类型直接包含该类型的成员),但您仍然可以将该类型设置为公共类型的私有内部类型,不允许使用该类型以外的用法(而且是朋友)。

如您所见,私人和公众的含义以及可以用它控制的内容存在差异。通过不在标题中提供定义并使用未命名的命名空间,您将无法访问到其他TU,只提供前向声明,其类型已知存在,但不是可以在任何类型必须完成的上下文中使用(函数仍然可以获取并转发指向该类型的指针)。在另一个平面上,通过将其设置为内部且private为不同类型,定义将已知但它不能在封闭类型的friend之外使用...

答案 3 :(得分:1)

目前还不清楚你想要实现的目标。但是你已经被问到这个问题,并且未能澄清它。在您的回答评论中,您写道“我的意思不仅是[禁止]实例化,而且是对类的任何类型的引用(例如在函数中将其用作参数类型)”。

采用面值,这意味着在头文件中使用以下内容:

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来完成。

另请注意,此方法仅支持标题模块;无需单独编译。 : - )

哦,对于一般情况,你还需要为数组添加分配器函数。

干杯&amp;第h。,

答案 4 :(得分:1)

嗯...

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
}

这就是你想要的吗?

P.S。请注意,无法隐藏myclass_impl,因为boost::shared_ptr<T>提供对基础类型的访问权。