2017-04-03 67 views
2

棘手的问题。如果想写一个函数返回一个指针到一些IFoo对象,是否有可能阻止这个指针的赋值? 我不想让IFoo成为一个单例,我可以隐藏或删除复制和赋值操作符,但C++实际上是否允许一个模式,我明确地必须调用其他人来获取对象?有没有办法来防止分配指针?

背景的问题是:我正在考虑某种依赖容器,你应该始终容器得到一些IFoo *(指针多态性的缘故)的。用户永远不应该能够将其保存到某个局部变量或成员,以避免引用它。 (对于其中容器被指示不再Foo返回场景,这是从IFooBar衍生)

编辑澄清,虽然者R萨胡已经说过,是不可能的。 事实上,马克B的例子是什么,我想阻止一个完美的例证:

IFoo* foo_ptr_I_will_keep_forever = obj->getIFoo(); 

我不会有接口,但类型的唯一明确的情况下,我可以返回一个参考,这给私人经营=和复制ctor就足够了。

+5

我在处理精确问题时遇到了一些麻烦。你问是否有办法阻止用户说'IFoo * foo_ptr_I_will_keep_forever = obj-> getIFoo();'?一个简短的例子可能会澄清你的问题。 –

+0

指针如何使用? – YSC

+0

您是在谈论一个指向只读对象的指针,并且不允许客户端写入对象? –

回答

2

您的标题说:

有没有一种办法,以防止指针赋值?

不,你不能阻止,如果你的函数返回一个指针。

但是,如果您返回一个句柄,该句柄可以是仅指向前面声明的类型的指针,也可以是可用于创建实际对象的整型值,并确保所有实际功能均可用句柄,那么当你可以删除真实的对象时,你可以有更多的自由度,而不会留下带有悬挂指针的客户端代码。

下面是一个简单的程序,演示了这个概念。

#include <iostream> 
#include <set> 

// Foo.h 

struct Foo; 
using FooHandle = Foo*; 

FooHandle createFoo(); 

void fooFunction1(FooHandle h); 

void fooFunction2(FooHandle h); 

// Test Program 

int main() 
{ 
    FooHandle h = createFoo(); 
    fooFunction1(h); 
    fooFunction2(h); 
    fooFunction1(h); 
    return 0; 
} 

// Foo implementation. 

namespace FooImpl 
{ 
    std::set<Foo*>& getLiveFooObjects() 
    { 
     static std::set<Foo*> liveObjects; 
     return liveObjects; 
    } 

    bool isValid(Foo* h) 
    { 
     return (getLiveFooObjects().find(h) != getLiveFooObjects().end()); 
    } 
} 

using namespace FooImpl; 

struct Foo {}; 

FooHandle createFoo() 
{ 
    FooHandle h = new Foo{}; 
    getLiveFooObjects().insert(h); 
    return h; 
} 

void fooFunction1(FooHandle h) 
{ 
    if (isValid(h)) 
    { 
     std::cout << "In fooFunction1.\n"; 
    } 
    else 
    { 
     std::cout << "Ooops. The handle is no longer valid.\n"; 
    } 
} 

void fooFunction2(FooHandle h) 
{ 
    std::cout << "In fooFunction2.\n"; 
    delete h; 
    getLiveFooObjects().erase(h); 
} 

输出:

In fooFunction1. 
In fooFunction2. 
Ooops. The handle is no longer valid. 
+0

'不可能'是我想要的所有信息:)即使我个人不喜欢手柄解决方案,因为它会改变手柄的使用范例并使其使用起来相当复杂。 – Samuel

2

给他们一个对象(他们可以存储,如果他们想要的话),通过私人(朋友)接口总是查找真实的对象。

例如,IFooCaller通过获取当前的IFoo并转发所有呼叫来实现IFoo

1

一个中间地带的答案,将防止意外存储一个指向一个特定的实现,但不会阻止其他人这样做是故意的:

template <typename T> class service_wrapper; 

class service_manager 
{ 
    template <typename T> friend class service_wrapper; 

public: 
    template <typename T> 
    service_wrapper<T> get() const; 

private: 
    template <typename T> 
    T* get_instance() const; 
}; 

template <typename T> 
class service_wrapper 
{ 
    friend class service_manager; 

public: 
    T* operator->() const; 

private: 
    service_wrapper(service_manager const & p_sm) : sm(p_sm) { } 
    service_manager const & sm; 
}; 

template <typename T> 
T* service_wrapper<T>::operator->() const 
{ 
    return sm.get_instance<T>(); 
} 

您的经理只会分配service_wrapper<T>的实例。operator->实现允许使用wrapper->method(...);调用服务,并始终从服务管理器获取实现指针。

这可以规避,如:

T *ptr = sm.get<T>().operator->(); 

但是,这不是你可以做的意外。

相关问题