没有共享所有权的Getters

时间:2009-10-20 21:18:04

标签: c++ getter

如何编写无法删除的getter? 我想拥有变量而不是分享它们。 在这里和那里读书我发现无论我什么回来,记忆都可以被释放

然而我定义它,这是真的吗?

引用,const指针,无论如何,调用getter的函数都可以删除它,而我的私有变量不会被取消但是内存坏了,对吗?

我想开发一个getter,我可以返回我的私有变量,并确保被调用者无法删除它...

我担心,在内部使用私有变量时,被调用者已将其销毁,然后在我内部的下一次尝试使用它时崩溃了我的程序

在第一次尝试中我不想使用boost,因为我试图从这个项目中学到最多,如果没有其他方式可以使用boost,或者如果反过来太复杂/多工作

谢谢,

我的另一个问题并没有真正集中,所以我再次这样做了,问这里问题不是问题,对吧? =]

9 个答案:

答案 0 :(得分:5)

取决于你的意思。只要有指针,可能就可以在其上调用delete

如果你有一个引用,你可以获取它的地址,它会给你一个指针

无论如何,如果你有这个类,例如:

class X {
  int getByVal() { return i; } // returns a copy of i
  int& getByRef() { return i; } // returns a reference to i
private:
  int i;
};

然后,作为您班级的用户,我没有明显的方法来删除您的数据。 我可以做到以下几点:

X x;
int j = x.getByVal();
int& k = x.getByRef();
j = 42; // doesn't affect x.i because we returned a copy
k = 42; // sets x.i to 42, because k is a reference

我没有明显的方法可以删除班级成员。当然,我可以这样做:

delete &j;
delete &k;

(当然,这些都不会有任何意义,但他们会编译)但我不会偶然这样做。如果你没有返回指针,很明显我不应该取得数据的所有权。

“保护您的代码免受Murphy,而不是Machiavelli”通常是一个很好的经验法则。如果他们尝试,您无法阻止人们破坏您的代码。你应该担心的是阻止他们意外地做这件事。

修改
在回答您对该问题的评论时:

  

正如我所说,我正在学习......副本认为被调用者必须释放返回变量的内存,这对被调用者来说更麻烦(甚至认为它是me = p),所以我不是谈论概念,但写作的简单性...再次,我是这个记忆的新手。我正在使用C#,PHP等进行开发。很久以前,当我在使用CircleMUD学习时,我曾经在C中开发

不,不必手动删除副本。局部变量超出范围时会自动删除。因此,在上面的示例中,j是类成员i的副本。当调用函数返回时,j将被自动删除。

希望有所帮助。 C ++中的变量生命周期规则并不是很复杂,但要使它们正确是非常重要的,因为很多代码都依赖于它们。

void foo()
{
  int i = 0; // allocate a local (on the stack) int, and initialize it to 0
  int* p = new int(1); // allocate an int on the heap, and initialize it to 1
  int j = i; // create a *copy* of i. Now we have two ints on the stack
  int k = *p; // create a copy of the int pointed to by p. k is also on the stack, so even though it was copied from a heap-allocated variable, k does not have to be manually deleted
  int* q = p; // create a copy of p. q is not a separate pointer, which points to the *same* heap-allocated integer.
}

在上面的示例中,foo返回时会自动清除所有副本。我们手动做的唯一事情是删除我们在堆上分配的整数。 pq都指向它,但我们只能删除一次对象。 但ijkpq都是在堆栈上声明的局部变量。当函数返回时,每个都被清理。对于原始类型(例如int以及指针),实际上不必发生任何事情(它们没有析构函数)。当它们超出范围时,它们就会消失 - 即使它们指向一些重要的东西,比如堆分配的对象,例如我们的整数。

对于非POD对象,当它们超出范围时,它们的析构函数被调用,因此它们也可以很好地清理,完全由它们自己完成。因此,即使我们使用了比int更复杂的类型,上面的工作也会很好。我们仍然可以复制非POD对象并按值传递它们。

我希望这有助于澄清一些事情。

答案 1 :(得分:3)

嗯,最安全的方法是按值返回对象的副本:

MyObject GetMyObject() const {return _myObject;}

然后调用者可以用他的副本做任何他想做的事情,这不会影响你。

当然,这确实会导致复制对象的开销......这是否重要取决于对象的复杂程度(因此复制对象的成本有多高)。

下一个最好的方法是返回一个const引用:

const MyObject & GetMyObject() const {return _myObject;}

这将为被调用对象提供一个引用,该对象在技术上可以被删除但通常不会被删除(假设在返回引用时没有传递所有权)。

您可以做的最后一件事是使用引用计数来返回对象的引用。在这种情况下,没有人需要删除该对象,因为当对象的最后一次引用消失时,该对象将被自动删除。查看boost的shared_ptr类了解详细信息;我强烈建议将引用计数作为管理C ++中内存分配的一种方法,因为它可以自动消除程序员在管理内存时可能产生的99%的错误。

答案 2 :(得分:3)

在C ++中,您无法阻止调用者崩溃程序。周期。

我建议只返回一个参考。如果呼叫者去了并取了那个地址并将其传递给delete,那么他们就会遇到麻烦 - 如果他们想要破坏它们,那么你就无法阻止它们。

答案 3 :(得分:1)

你可以使用弱指针。弱指针指的是共享智能指针所拥有的资源,而不会添加其引用计数。 (但是,只要用户可以从智能指针实现裸指针 - 没有它,它将只是智能,而不再是指针 - 它们可以在那上面调用delete。但是没关系:如果人们想要破坏他们的代码,他们总会想办法做到这一点。看看你的目标是阻止墨菲做他的工作,而不是Macchiavelli。

当然,您也可以返回参考。 (与上述相同的免责声明适用。)

如果您的对象副本便宜,您也可以返回副本。 (而且,从理论上讲,仍然可以将副本的地址传递给delete。)

答案 4 :(得分:0)

如果按值返回,则来电者将无权访问您的内部成员。

答案 5 :(得分:0)

真正临时的解决方案可能是重载operator new和delete(也许是new [] / delete []),使delete运算符变为私有并仅授予对所有者类的访问权限:

#include <memory>

class Y
{
private:
    void* operator new(size_t size) { return new char[size]; }
    void operator delete(void* p) { delete [] static_cast<char*>(p); }
    friend class X;
};

class X
{
    Y* p;
    X(const X& x);
    const X& operator=(const X& x);
public:
    X(): p(new Y()) {}
    ~X() { 
        delete p;  //OK here, X is a friend
    }
    const Y* get() const { return p; }
};

int main()
{
    X x;
    const Y* p = x.get();
    delete p;  //error here: operator delete is private
}

然而,只要你没有返回一个错误可能是可以想象的裸指针,如果你要返回一个智能指针,并且调用者仍然决定释放托管指针,那么随后发生的一切将是他们的问题。几乎每当你看到删除调用时,你都知道它一定是个错误。

答案 6 :(得分:0)

您是否考虑过找一些编写删除其他模块变量的代码的人,并将它们喷涂到puce或类似的东西上?

说真的,如果你的同事绝对决定要破坏它们,那么你真的没什么可做的(除非他们当场解雇,如果你是老板)。哎呀,不能保证你传出的东西是单独分配的东西,如果调用者在随机地址上使用delete,就不知道会发生什么(除了某种形式的堆腐败几乎可以肯定)。

没有编程语言构造或编程语言,这将使你从这样的同事中获益。 “反对愚蠢,众神本身就是徒劳无功。”

答案 7 :(得分:0)

我不确定这是否是您正在寻找的,这也取决于您真正需要需要多少努力,但您总是可以使用pimpl idiom

总结您不想公开的成员,并将句柄传递给它。句柄只是轻量级的包装器,可以按值快速传递,每个句柄只指向相同的底层唯一数据,只有你的内部代码可以实际创建和销毁。

例如,

class Interface
{
public:
    Data getData() const { return Data( m_data ); }

private:
    DataImpl* m_data;
};

,其中

class Data
{
public:
    // Just use compiler-synthesised copy ctor, assignment and dtor

    // Declare as many forwarding methods to impl as required
    // ...

private:
    friend class Interface;
    Data( DataImpl* d ) : m_impl( d ) {}
    DataImpl*const m_impl;
};

这里的简单示例是天真的,因为当Data最终被破坏时它不会使任何现有的DataImpl句柄无效,因此它们会留下陈旧的指针。这可以通过更多的努力来解决,但是你又要留下它是否真的值得的问题。

答案 8 :(得分:-1)

如果你有一个拥有所有权的对象(银行开帐户(我知道的话)) 但人们想要访问该帐户,但您不希望他们删除它。

class Bank
{
    public:
        // use auto_ptr to indicate transfer of ownership to the Bank.
        void addAccount(std::auto_ptr<Account> a)
        {
            m_account = a;
        }
        //
        // getAccount() returns a reference to the object
        // Anbody using the object can NOT now delete it.
        // But they can manipulate it.
        Account&  getAccount(int accountNo)
        {
            return *m_account;
        }
    private:
        std::shared_ptr<Account>   m_account;  // In example bank only has 1 account
}