开发者

Getters without shared ownership

How to write a getter that can not be deleted? I want to own the variables and not share them. reading here and there I figured out that no matter what I return the memory can be freed

however I define it, is this true?

references, c开发者_运维技巧onst pointers, no matter what, the function which is calling the getter can delete it and my private variable would not be nullified but with broken memory, right?

I would like to develop a getter where I can return my private variable and be sure that the callee can't delete it...

I am afraid that, while internally using the private variable, the callee has destroyed it and then it crashes away my programm on my internal next attempt to use it

in a first attempt I wouldn't like to use boost, as I am trying to learn the most from this project, boost would be used if not other way around or if the other way around is too complex/much-work

Thanks,

Joe

My other question wasn't really focused so I did it again, its not a problem to asks things here, right? =]


Depends on what you mean. Any time you have a pointer, it is possible to call delete on it.

And if you have a reference, you can take the address of it, which gives you a pointer

Anyway, if you have this class for example:

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

then I, as a user of your class, do not have an obvious way to delete your data. I can do the following:

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

And there's no obvious way for me to delete the class member. Of course, I could do this:

delete &j;
delete &k;

(and of course, neither of these would do anything meaningful, but they would compile) but I wouldn't do so by accident. If you don't return a pointer, it's pretty clear that I'm not supposed to take ownership of the data.

"Protect your code against Murphy, not Machiavelli" is usually a good rule of thumb. You can't prevent people from wrecking your code if they try. All you should worry about is preventing them from doing it accidentally.

Edit
In response to your comment under the question:

as I said, I am learning... Copies make think that the callee must free the memory of the returning variable, which is more trouble to the callee(even thought it is me =p), so I wasn't talking about concepts, but the easyness of writing... and again, I am newbie on this memory stuff. I was developing in C#, PHP etc. I used to develop in C long time ago when I was learning with CircleMUD

No, copies don't have to be deleted manually. Local variables are automatically deleted when they go out of scope. So in the above example, j is a copy of the class member i. When the calling function returns, j will be automatically deleted.

Hope that helps. The variable lifetime rules in C++ are not very complicated, but it is extremely important to get them right as a lot of code depends on them.

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.
}

in the above example, all the copies are automatically cleaned up when foo returns. The only thing we have to do manually is to delete the integer we allocated on the heap. Both p and q point to it, but we must only delete the object once. But i, j, k, p, and q are all local variables, declared on the stack. Each of them are cleaned up when the function returns. For primitive types (such as ints as well as pointers), nothing really has to happen (they don't have destructors). When they go out of scope, they just disappear - even if they pointed to something important, like a heap-allocated object such as our integer.

For non-POD objects, when they go out of scope, their destructors are called, so they too get cleaned up nicely, all by themselves. So even if we'd used a more complex type than int, the above would have worked just fine. We can still copy non-POD objects and pass them by value.

I hope that helps clear things up a bit.


Well, the safest way to do that is to return a copy of your object by value:

MyObject GetMyObject() const {return _myObject;}

Then the caller can do anything he wants with his copy, and it won't affect you.

Of course, that does incur the overhead of copying the object... whether that's important or not depends on how complex the object is (and thus how expensive it is to copy it).

The next best thing is to return a const reference to it:

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

That will give the called a reference to the object, which can technically be deleted but generally won't be (it's assumed that no ownership is passed when returning a reference).

The final thing you could do is use reference counting to return a reference to the object. In that case, nobody ever needs to delete the object, since the object will be automatically deleted when the last reference to it goes away. Check out boost's shared_ptr class for details on this; I highly recommend reference counting as a way of managing memory allocation in C++, since it automates away 99% of the potential errors programmers make when managing memory.


In C++, there is nothing you can do to prevent the caller from crashing your program. Period.

I would suggest just returning a reference. If the caller goes and takes the address of that and passes it to delete, they're asking for trouble - and if they're that intent on breaking things, there's not much you can do to stop them.


You can use weak pointers for that. Weak pointers refer to a resource owned by a sharing smart pointer without adding to its reference count. (However, as long as users can achieve a naked pointer from a smart pointer - and without that, it would only be smart, but not a pointer anymore -, they can invoke delete on that. But that's OK: If people want to crash their code, they'll always find ways to do this. See your goal in preventing Murphy from doing his work, not Macchiavelli.

Of course, you can just as well return a reference. (The same disclaimer as above applies.)

If your objects are cheap to copy, you can also return a copy. (And it's, syntqactically, still possible to pass the copy's address to delete.)


If you return by value, the caller will not have any access to your internal member.


A really ad hoc solution might be to overload operator new and delete (and perhaps new[]/delete[]), making the delete operator private and granting access only to the owner class:

#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
}

However, as long as you don't return a naked pointer where a mistake might be imaginable, if you were to return a smart pointer, and the caller still decided to free the managed pointer, everything that ensues will be their problem. Pretty much any time you see a call to delete, you'd know it must be an error.


Have you thought about finding people who write code that deletes other module's variables, and spray-painting them puce or something like that?

Seriously, if your colleagues are absolutely determined to break things, there's really not much you can do about it (except fire them on the spot, if you're the boss). Heck, there's no guarantee that what you're passing out is something that was separately allocated, and if the caller uses delete on a random address there's no telling what will happen (except that some form of heap corruption is almost certain).

There's no programming language construct, or programming language for that matter, which will save you from colleagues like that. "Against stupidity the gods themselves contend in vain."


I'm not sure if this is what you are looking for and it also depends on how much effort you really need to take but you could always use the pimpl idiom.

Wrap up the member you don't want to expose and just pass out handles to it. The handles are just lightweight wrappers that can be merrily passed around by value, each handle simply points at the same underlying unique piece of data which only your internal code can actually create and destroy.

For example,

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

private:
    DataImpl* m_data;
};

where

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;
};

The simple example here is naive since it does not invalidate any existing Data handles when the DataImpl is ultimately destroyed and so they are left with stale pointers. This can be solved with more effort but again you're left with the question of whether it's really worth it.


If you have an object that takes ownership (A bank takes an account (contrived I know)).
But people want access to the account but you dont want them to delete it.

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
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜