I have a class that contains a pointer. I want to keep the user of the class from accessing the address of the pointer (so they can't set it to another address, delete it, or what-not). However, I would like the user to be able to modify the pointer data (or member data if it's not POD) as well as call the pointer's methods (assuming it has any).
Is there any way of returning a pointer or reference that allows you to change the data that a pointer points to without being able to change the pointer value itself?
So:
class A
{
public:
int Value;
void Method();
};
class Wrapper
{
public:
Wrapper()
{
Pointer = new A;
}
// Method that somehow would give access to the object without
// Allowing the caller to access the actual address
A* GetPointer()
{
return Pointer;
}
private:
A* Pointer;
};
int main()
{
Wrapper foo;
foo.GetPointer()->Value = 12; // Allowed
foo.GetPointer()->Method(); // Allowed
A* ptr = foo.GetPointer(); // NOT Allowed
delete foo.GetPointer(); // NOT Allowed
return 0;
}
I realize I could modify member data with getters and setters, but I'm not sure what to do about the methods (pass a method pointer maybe?) and I'd like to know if there is a better way before I accept a solution that I personally think looks messy.
It's not possible. The whole reason why ->Value is legal is because the expression to the left is a (smart) pointer to A*.
Obviously, with a non-smart pointer you already have your A* right there. Since raw pointers are not user-defined types, you cannot mess with the overload resolution.
With a smart pointer, (*ptr).Value has to work. That means you have to return a A& from operator* which in turn means that &(*ptr) gets you the traw pointer from a smart pointer.
There's even std::addressof for classes that try to block operator&.
You could make a getter That returns a reference to the object, ex:
A &GetObject()
{
return *Pointer;
}
This allows full access to the pointed-to object without providing access to the pointer itself at all.
Related
Are smart pointers considered as pointers? And thus can they implicitly used as pointers?
Let's say I have the following class:
class MyClass {
//...
std::shared_ptr<AnotherClass> foo() { /*whatever*/ };
void bar(AnotherClass* a) { /*whatever too*/ };
//...
}
Then can I use MyClass the following way?
// m is an instance of MyClass
m.bar(m.foo());
No they can't be used interchangable. You would get a compiler error in your example. But you can always get the raw pointer by shared_ptr::get().
NO! It would be a terrible API. Yes, you could easily implement it within shared_ptr, but just because you could doesn't mean you should.
Why is it such a bad idea? The plain-pointer-based interface of bar doesn't retain an instance of the shared pointer. If bar happens to store the raw pointer somewhere and then exit, there's nothing that guarantees that the pointer it had stored won't become dangling in the future. The only way to guarantee that would be to retain an instance of the shared pointer, not the raw pointer (that's the whole point of shared_ptr!).
It gets worse: the following code is undefined behavior if foo() returns a pointer instance that had only one reference when foo() returned (e.g. if foo is a simple factory of new objects):
AnotherClass *ptr = m.foo().get();
// The shared_ptr instance returned by foo() is destroyed at this point
m.bar(ptr); // undefined behavior: ptr is likely a dangling pointer here
Here are the options; consider those listed earlier first before considering their successors.
If bar(AnotherClass *) is an external API, then you need to wrap it in a safe way, i.e. the code that would have called Original::bar should be calling MyWrapped::bar, and the wrapper should do whatever lifetime management is necessary. Suppose that there is startUsing(AnotherClass *) and finishUsing(AnotherClass *), and the code expects the pointer to remain valid between startUsing and finishUsing. Your wrapper would be:
class WithUsing {
std::unique_ptr<AnotherClass> owner; /* or shared_ptr if the ownership is shared */
std::shared_ptr<User> user;
public:
WithUsing(std::unique_ptr<AnotherClass> owner, std::Shared_ptr<User> user) :
owner(std::move(owner)), user(std::move(user)) {
user.startUsing(owner.get());
}
void bar() const {
user.bar(owner.get());
}
~WithUsing() {
user.finishUsing(owner.get());
}
};
You would then use WithUsing as a handle to the User object, and any uses would be done through that handle, ensuring the existence of the object.
If AnotherClass is copyable and is very cheap to copy (e.g. it consists of a pointer or two), then pass it by value:
void bar(AnotherClass)
If the implementation of bar doesn't need to change the value, it can be defined to take a const-value (the declaration can be without the const as it doesn't matter there):
void bar(const AnotherClass a) { ... }
If bar doesn't store a pointer, then don't pass it a pointer: pass a const reference by default, or a non-const reference if necessary.
void bar(const AnotherClass &a);
void bar_modifies(AnotherClass &a);
If it makes sense to invoke bar with "no object" (a.k.a. "null"), then:
If passing AnotherClass by value is OK, then use std::optional:
void bar(std::optional<AnotherClass> a);
Otherwise, if AnotherClass takes ownership, passing unique_ptr works fine since it can be null.
Otherwise, passing shared_ptr works fine since it can be null.
If foo() creates a new object (vs. returning an object that exists already), it should be returning unique_ptr anyway, not a shared_ptr. Factory functions should be returning unique pointers: that's idiomatic C++. Doing otherwise is confusing, since returning a shared_ptr is meant to express existing shared ownership.
std::unique_ptr<AnotherClass> foo();
If bar should take ownership of the value, then it should be accepting a unique pointer - that's the idiom for "I'm taking over managing the lifetime of that object":
void bar(std::unique_ptr<const AnotherClass> a);
void bar_modifies(std::unique_ptr<AnotherClass> a);
If bar should retain shared ownership, then it should be taking shared_ptr, and you will be immediately converting the unique_ptr returned from foo() to a shared one:
struct MyClass {
std::unique_ptr<AnotherClass> foo();
void bar(std::shared_ptr<const AnotherClass> a);
void bar_modifies(std::shared_ptr<AnotherClass> a);
};
void test() {
MyClass m;
std::shared_ptr<AnotherClass> p{foo()};
m.bar(p);
}
shared_ptr(const Type) and shared_ptr(Type) will share the ownership,
they provide a constant view and a modifiable view of the object, respectively. shared_ptr<Foo> is also convertible to shared_ptr<const Foo> (but not the other way round, you'd use const_pointer_cast for that (with caution). You should always default to accessing objects as constants, and only working with non-constant types when there's an explicit need for it.
If a method doesn't modify something, make it self-document that fact by having it accept a reference/pointer to const something instead.
Smart pointers are used to make sure that an object is deleted if it is no longer used (referenced).
Smart pointer are there to manage lifetime of the pointer they own/share.
You can think of a wrapper that has a pointer inside. So the answer is no. However you can access to the pointer they own via get() method.
Please note that it is not so difficult to make dangling pointers if you use get method, so if you use it be extra cautious.
I'm trying to keep a reference to a pointer of a different class in my class. I'm trying to see if there is a way to do this without having to define it in the ctor. I cannot make a copy, as I'm using that specific pointer returned to do other things.
class MyClass {
private:
OtherClassPtr &m_ptr_ref;
public:
MyClass();
public:
void MyFunction() {
m_ptr_ref = otherClassPtr->GetPtrRef();
if(!m_ptr_ref)
return;
}
};
A reference needs to be initialized at the point of declaration, and cannot change to refer to a different object during its lifetime. Thus you need to set it in the constructor.
An alternative is to store a pointer. I think of a reference as a pointer with nicer syntax, though the different syntax gives it a different semantic meaning; it acts like the object that it refers to, and so has the same value and the same address as that object. Most relevant to your question, the assignment operator works like assignment to the object, rather than a pointer. This is the reason it cannot change referent.
You can keep a pointer to the pointer:
OtherClassPtr* m_ptr_ref;
/* ... */
m_ptr_ref = &otherClassPtr->GetPtrRef();
An alternative is to use std::reference_wrapper, but that is nothing more than a fancy pointer, and I don't see the advantage over using a pointer.
I was wandering through the code of Sequitur G2P and found a really strange line of code:
public:
...
const Node *childrenEnd() const { return (this+1)->finalized.firstChild_; }
I know that this is a pointer to the current object, and since it is a pointer, the operation is perfectly legal, but what does this+1 actually refer to?
Presumably this is part of an array, so this+1 would refer to the next object in that array.
this is simply a pointer which refers to this object. Since it's a pointer, you can apply pointer arithmetic and even array indexing.
If this object is an element in an array, this+1 would point to the next object in the array.
If it's not, well it's just going to treat whatever is at that memory the same as this object, which will be undefined behaviour unless it is the same type.
As it is NLP it makes sense to optimize memory management. I assume you find overloaded new/delete methods as well.
The this+1 construct assumes all objects reside in an array. The name 'childrenEnd' of the method indicates it returns a pointer to an address of the end of the children of the current node.
Thus you are looking at an implementation of a tree structure. All siblings are adjacent and their children as well.
"this + 1" in C++ class means:
if the "this" object is a member of another object it will point to the address of the parent's object next variable declared just after the "this" object variable:
Example:
class B
{
public:
void* data()
{
return this + 1;
}
};
class A
{
public:
B m_b;
char m_test;
};
int main(int argc, char* argv[])
{
A a;
a.m_test = 'H';
void* p = a.m_b.data();
char c;
memcpy(&c, p, sizeof(char));
return 0;
}
c is equal 'H'.
Long story short it allows to access to parent's class data without passing parent's pointer to the child class. In this example this + 1 point to the m_test member of the class A.
Actually, there is a case, when this thing could be used. I don't recommend to use this method, but it certainly works.
I believe, in NLP code it was used something like that:
when you want your object to behave as a collection (an array etc) to use it similarly as an array with something range-based etc, you can do this trick:
struct Obj {
...
Obj* begin() { return this; }
Obj* end() { return this+1; }
...
}
Now, you can use this object in, for example, range-based for-loops...
Sometimes all that is necessary... but just even there you'd better use "nullptr" or even do refactoring than to use this trick.
I understand that std::unique_ptr is the way it is and probably won't be changed to break backwards compatibility but I was wondering if anyone has a good reason why the writers of the spec didn't overload the get method with a const variant that looks like
const T* get() const;
to follow the intent of the unique_ptr being const.
My best guess is that it is trying to mirror pointers and act like a T* const instead of a typical class. As a follow-up question, if I wanted to hold a pointer in a const-like fashion in a const instance of my class, should I be using something else other than std::unique_ptr to hold the data?
Update
In my case I want to protect myself from misusing the pointer in the class itself. I was writing a const move constructor MyClass(const MyClass&& other) and was copying the data from the new instance into other via std::copy. It took a long time to track down the bug because I had assumed the copy must be correct because of const protection. I'm trying to figure out what I could have done to protect myself from this outside of providing a const getter and using that within the class when doing the copy.
Smart pointers are pretending to be a raw pointer.
If you have class member which is raw pointer and use it in const method that you can't update a pointer, but you can modify object which is pointed.
Same behavior is desired for smart pointer. So std::unique_ptr::get is a const method, but doesn't force to return pointer to const object.
Note also that you can have a pointer to const object.
MyClass *pointerToObject
std::unique_ptr<MyClass> smartPointerToObject;
// but you can have also a case
const MyClass *pointerToConstObject
std::unique_ptr<const MyClass> smartPointerToConstObject;
In last case std::unique_ptr::get will return something you are expecting.
Based on comment below:
Just provide private methods:
InnerClass& GetField() { return *uniquePtrToInnerClass; }
const InnerClass& GetField() const { return *uniquePtrToInnerClass; }
And use it in your code and you will have const object of inner class in const method.
There's no point to giving read-only access to an object via its unique_ptr. You only pass unique_ptr around when you are transferring ownership, for access to the object without an ownership transfer, call up.get() and pass a const T* to the function that should only read (or if the pointer is never nullptr, it's also reasonable to evaluate *(up.get()) and pass a const T&).
As a bonus, this allows you to use that function with objects stored on the stack, embedded inside another object, or managed with a smart pointer other than unique_ptr.
There's a good discussion of all the unique_ptr parameter passing cases (in/out, const/non-const, etc) here:
How do I pass a unique_ptr argument to a constructor or a function?
For the same reason a T*const when dereferenced is a T&, not a T const&.
Constness of pointer is distinct from pointness of pointed-to.
get is const, it does not modify the state of unique_ptr.
Its constness does not impact the constness of the contents.
There is the idea of smart pointers that propogate constness, but unique_ptr is not that beast.
std::experimental::propogate_const wraps a pointer-like object and makes const travel through it.
It, or something like it, may solve your problem.
Note that I find half the time when I try to have const propogate like this, I discover I was wrong. But this may not be the case here.
In general, the proper way to handle the guts of a T*const in a const manner is to pass a T const& (or the nullable variant T const*).
I think this concern is valid,
there should be 2 versions for each de-referencing functions,
e.g.
const T* get() const;
T* get();
enter code here
I know that purpose of providing "T* get() const" is to ease replace existing raw pointer usages.
But since uniq ptr denotes ownership, it is incorrect that some one being able to modify some thing OWNED by the object via a immutable(const) reference [assuming modifying something fully owned by a object is same as modifying the object itself - which is true if this was an object instead of a ptr].
May be best option would be std to provide another version of Uniq ptr which holds to above idiom (only other option may be to derive a new class from uniq ptr and provide 2 versions for de-referencing )
Because as far as the unique_ptr is concerned, getting the internal raw pointer reference is a const operation. Calling .get() and retrieving the internal raw pointer of a std::unique_ptr does not change the internal state of the std::unique_ptr object itself. So it seems the library designers elected to mark it const without attention to what could happen to the underlying object if they just return a straight non-const reference to it.
In fact, if you have a std::unique_ptr inside an object, and you call a const member function of that object, you can still call non-const member functions on the internal std::unique_ptr inside that object. For example:
struct A {
void Const() const { }
void nonConst() { }
};
struct B {
std::unique_ptr<A> a;
void go() const {
a->nonConst(); // OK
}
};
Although you cannot perform non-const operations on the internal state variables of an object from one of its const member function, there is no rule that says you cannot perform non-const operations on other objects.
What you may be expecting is the constness promise to carry over from the unique_ptr to also apply to access to what it internally points to, so you'd expect unique_ptr to be written something like this:
template <typename T>
class cunique_ptr {
T* ptr;
public:
cunique_ptr() {
ptr = new T;
}
~cunique_ptr() {
delete ptr;
}
// You can only get a non-const pointer to the internals from a non-const object
T* get() { return ptr; }
// The const member function carries over the const promise to access to its internals
const T* get() const { return ptr; }
};
void test() {
cunique_ptr<A> a;
a.get()->nonConst();
const cunique_ptr<A> ca;
//ca.get()->nonConst(); //X fails: cannot call non-const member functions from const object
ca.get()->Const();
}
However, it seems the library designers elected against that type of protection and let the const promise be kind of shallow as it were.
I have run into a strange problem. When i try to write a function that returns a pointer to a subclass which i swap for a different instance of that subclass i get the "error C2106: '=' : left operand must be l-value" error.
Tthe problems is at the one->getMe() = two; line :
class subClass{};
class someClass{
public:
subClass * pointer;
someClass(){
pointer = new subClass;
}
subClass * getMe(){
return pointer;
}
};
int main(){
someClass * one = new someClass;
subClass * two = new subClass;
one->getMe() = two;
}
I'm a bit new to c++ so the problem might be horrible simple. Any help is much appreciated.
The pointer that you return from the function is a copy of the pointer in the object. It is a temporary, you cannot assign to it. Even if you could assign to it, it would do no good, because it is no longer connected to the pointer in your object. If you want to be able to assign to the object's pointer after returning it from the function, you want to return the pointer by reference.
subClass *& getMe() {
return pointer;
}
You should also may want to consider adding a const overload in order to work on const objects. It might look like this:
subclass * const & getme() const {
return pointer;
}
The line
one->getMe() = two;
Is trying to assign the pointer at two to one->getMe(). But one->getMe() returns a temporary copy which you cannot assign to. If you want to return the pointer by reference and thus be able to change it then you can use the signature
subClass *& getMe();
as others have suggested.
That being said, you're exposing the inner workings of a class for all and sundry to see and modify, in particular pointers to objects on the heap which may or may not be deallocated correctly which is unwise.