A pointer to a const object doesn't allow to change the object. For example:
class foo {
public:
int m_data;
foo() : m_data{11} {}
};
void change(const foo *obj)
{
obj -> m_data = 21; // gives error, normal
}
This is fine and normal behavior.
What I don't understand is that when we add an additional pointer variable, either of the same type (like foo *) or of some other type (like bar *), but not the basic types like int *, then we can change the value that the pointer points to (from a pointer to the const type itself).
How is this possible?
class foo {
public:
int m_data;
foo *m_next;
foo() : m_data{11}, m_next{nullptr} {}
};
void change(const foo *obj)
{
obj -> m_next -> m_data = 21; // does not give any error! why?
}
This also happens if we take some other type inside foo, like bar *.
Why is this happening? How can I define the function so that I can be sure that my function can't change the value the object is pointing to?
obj -> m_next -> m_data = 21; // does not give any error! why?
Because this is not changing the object. This object's m_next pointer is exactly the same as it was before.
This is only changing whatever m_next is pointing to. Which has nothing to do, whatsoever, with the object that owns this m_next pointer.
Now, if, on the other hand, you try to assign something to the m_next pointer itself, instead of whatever it's pointing to, then you'll get the error you were expecting.
The only thing that the object contains are its enumerated members. None of them are changed by this line of code.
In your example, foo* m_next is a pointer member variable to a mutable foo object. The pointer itself is considered part of the object instance, not the object being pointed to.
By changing m_next, you are changing the pointer, which is considered part of the instance of foo. Doing something like obj->m_next = nullptr; in change would be invalid because you are changing the memory that is part of the constant foo instance. (Note: don't don't set random pointers to null in real code, unless you're sure it's safe, or it's a possible memory leak).
By accessing the object being pointed to by m_next via m_next->, you are accessing memory of another object that is not part of the const instance passed to change. This is perfectly valid.
You could say that constness is not transitive; a const object doesn't make objects accessed through it (via pointers) const.
Related
const references make sure you can't change the object you're referring to. For example:
int i = 1;
const int& ref = i;
ref = 42; // error, because of a const reference
But if you use a reference to a pointer or a unique_ptr, you can. Example:
class TinyClass {
public:
int var = 1;
void f1() { var = 42; }
};
std::unique_ptr<TinyClass> pointer(new TinyClass);
const std::unique_ptr<TinyClass>& constRef = pointer;
constRef->f1(); // no error
I assume this happens because the pointer itself wasn't changed. But this feels misleading, or like an easy mistake. Is there a simple way to declare a "real" const reference to a pointer? As in: makes sure the object itself is const.
edit: Assume that I can't just change the type of the unique_ptr. A real-world scenario would be that some objects are constructed in a vector<unique_ptr<C>> and some function gets a (const) reference to one of its elements.
The const in const std::unique_ptr<TinyClass>& constRef guarantees that constRef will not point to another object once it set up. It has nothing to do with the object itself.
If you want to prevent changing the object itself:
std::unique_ptr<const TinyClass> ptr_to_const_object;
Edit (after OP's edit):
You can not do anything. Since there is a function which wants const vector<unique_ptr<C>>&, the function clearly tells you that it needs to play with the object inside the pointer (and even the pointer) but it does not need to change the vector items (like adding new item or deleting it).
Consider this class
class Foo
{
public:
Foo()
: r_obj(&nullobj)
{}
void setDataRef(const T& obj)
{
r_obj = &obj;
}
private:
static T nullobj;
const T* r_obj;
};
This forces r_obj to point to something, and I know that the class cannot modify the logical state of whatever it points to. However, now it is perfectly legal to pass a temporary object to setDataRef which is really bad.
If instead using setDataRef(T&), the constness is lost.
Passing a pointer does not force the object to really point to something.
Is it possible to get all three:
Constness
Non-null
Forbid temporary
In old C++ I think this was impossible, but with C++11 we can do:
class Foo
{
public:
Foo()
: r_obj(&nullobj)
{}
void setDataRef(const T& obj)
{
r_obj = &obj;
}
void setDataRef(T&& obj) = delete; // Crucial extra line
private:
static T nullobj;
const T* r_obj;
};
If somebody tries to pass a temporary to setDataRef, overload resolution will prefer the T&& version but because it is deleted, the call is ill-formed. If somebody passes an lvalue, that will bind to the const reference.
Martin's suggestion is very nice and does prevent the caller from passing an rvalue to the function. But that still won't guarantee that the lifetime of the passed object extends beyond the lifetime of the Foo instance.
If the object stores a pointer to the passed object, it would be most intuitive, if a pointer were passed to the setter. Passing a pointer also prevents using an rvalue (because taking an address of an rvalue is not allowed).
Just like you have to document the interface that the passed object must exist as long as Foo instance exists, you can also document that the passed pointer must not be null. Additionally, you can assert it. You could similarly delete the overload for nullptr_t to prevent passing the literal to the setter but that won't prevent passing null pointers of T* type. In my opinion, the advantage of the reference setter is not great.
Of course, neither raw pointer, nor a reference tell the caller anything about ownership, so that must in either case be documented. Instead, I would recommend storing and passing a smart pointer. unique_ptr if Foo should have the ownership (apparently not), shared_ptr if Foo should keep the object alive if it goes out of scope elsewhere, or possibly weak_ptr if Foo should have a failure mode when referred object no longer exists. A smart pointer would be self documenting and intuitive for the user.
I am learning pointers in c++ and am having some trouble.
I have a class Foo that in the header file declares some data:
private:
const Bar *obj;
Where Bar is a class.
Then in the c++ implementation I want to replace *obj so that it points to a completely different Bar object. *obj is constant, so how do I change what is in what *obj points to or rather, what is in memory at *obj? Also in Foo's destructor, how do I deallocate *obj?
Given your class definition
class A {
private:
const Bar *obj;
};
obj is a pointer to a constant Bar object. You can change what that pointer points to, but you cannot change the contents of the object pointed to.
So, if you have a new Bar object and you'd like to change obj so it points to that, you can simply assign the new value:
/* New object: */
Bar *new_bar = new Bar;
/* Change the pointer: */
obj = new_bar;
There are two issues, however.
If the new Bar object is created outside the class, you cannot directly assign it to obj because the latter is private. Hence you need a setter function:
class A {
private:
const Bar *obj;
public:
void set_obj(const Bar *new_obj) { obj = new_obj; }
};
You must determine who will eventually own the Bar object, i.e. who is responsible for freeing the heap space it takes. If the caller is responsible then you can code it as above, i.e. class A will never create new Bar objects, nor delete any. It will just maintain a pointer to Bar objects created and deleted outside the class.
But if class A above is actually responsible for the memory space taken by the Bar objects, you must use delete obj in the destructor to free the space, and you must also free the space when you get a new Bar object assigned. That is, the set_obj function above needs to be changed to this:
void set_obj(const Bar *new_obj) { delete obj; obj = new_obj; }
Otherwise you'll have a memory leak. Similar measures must be taken in the copy constructor (unless you delete it), as well as the assignment operator: Both functions are used whenever a copy of a class A object is made, and in that case you must make sure that you do not simply copy the pointer, but instead allocate fresh space and copy the object (i.e. you must perform a deep copy):
A(const A& a):obj(new Bar(*a.obj)) {}
A& operator=(const A& a) { delete obj; obj = new Bar(*a.obj); return *this; }
Having said this, if your class is responsible for the memory space, it is a much better idea to use a smart pointer class instead of a raw pointer. The main reasons are: (i) The above is quite complicated and it's easy to make mistakes; (ii) The above is still not very good – there may still be memory leaks or worse problems when an exception is thrown, e.g. in the constructor of Bar. C++11 provides a smart pointer class called std::unique_ptr, which seems ideal for your purposes:
class A {
private:
std::unique_ptr<const Bar> obj;
public:
~A() {}
void set_obj(std::unique_ptr<const Bar> new_obj) { obj = new_obj; }
};
With this in place, the smart pointer will take care of any memory space that needs to be freed automatically, both at destruction time as well as when a new Bar object is assigned to the pointer.
You cannot use that pointer to to change the value that's being pointed to, that's why it's a const, but you should be able to change what it is pointing to.
On c++ "const Bar *obj;" means that you have a pointer to a readonly Bar object; meaning that you can point it to any readonly Bar object.
You can also point a non constant variable, thus promising that you will not change it using that pointer.
If you want to have a pointer, that is constant in the sense that it can't be made to point anything else, then you should write it this way:
Bar * const obj = some_object;
This will compile and work fine:
const int p = 1, q = 2;
int r = 3;
const int* i = &p;
i = &q; // point it to a different const variable.
i = &r; // point it to a different non const variable.
As written I believe that this is a pointer to a const Bar object and not a constant pointer.
I have edited this from my real code, so that it is a little easier to understand.
The base class:
class MWTypes
{
public:
virtual long get() { return (0); }
};
The derived class: (There are going to be other classes like char, double etc etc . . .)
class TypeLong : public MWTypes
{
public:
TypeLong(long& ref) : m_long(ref) {}
~TypeLong();
long get() { return m_long; }
private:
long& m_long;
};
and the storage class:
class RowSet
{
public:
void addElememnt(MWTypes elem);
MWTypes getElement();
std::vector<MWTypes> getVector() { return m_row; }
private:
std::vector<MWTypes> m_row;
};
How it is called:
for (i = 0; i < NumCols; i++) // NumCols is 3 on this instance
{
switch(CTypeArray[i]) // this is an int which identifies the type
{
case SQL_INTEGER:
{
long _long = 0;
TypeLong longObj(_long);
MWTypes *ptr = &longObj;
// some SQL code goes here that changes the value of _long,
// there is no need to include it, so this will do.
_long++;
// I now want to save the data in a vector to be returned to the user.
rowSet.addElememnt(*ptr);
///////////////////////////////////////////////
// some code happens here that is irrelevant //
///////////////////////////////////////////////
// I now want to return the typr I have saved in the vector,
// I THINK I am doing this right?
MWTypes returned = rowSet.getElement();
// lastly I want to get the value in the returned type
long foo = returned.get();
///////////////////////////////////////////////
// some code happens here that is irrelevant //
///////////////////////////////////////////////
I think I am on the right lines here. The value of 'foo' is always 0. I have a feeling this could be the way Im storing in the vector, or it could be the base virtual function, as it always returns 0.
If I remove the return in my base class I get LNK2001 errors.
MWTypes returned = rowSet.getElement();
// lastly I want to get the value in the returned type
long foo = returned.get();
should be
MWTypes* returned = &rowSet.getElement();
// lastly I want to get the value in the returned type
long foo = returned->get();
or
MWTypes& returned = rowSet.getElement(); // actually illegal, but MSVC will let you do
// lastly I want to get the value in the returned type
long foo = returned.get();
Indeed, polymorphic calls must be made via a pointer or a reference.
EDIT: this is not your only problem. The fact that the vector stores objects (and not pointers) will slice the objects and destroy their type information.
See this faq entry for additional info to help you solve your problem and understand how virtual functions are called.
The fundamental problem is that you are making copies of your objects of type MWTypes, thus losing their particular subclass. If you want to use an object of an unknown subclass of the base class, then you can only use a pointer or reference to the base type, not an actual instance of it.
Not providing an implementation of the function "get" as ascanio's code shows (making the function "pure virtual") would prevent you from being able to make this copying mistake, because the compiler would not let you instantiate the class MWTypes if you did that (it would say the class is "abstract").
You are suffering from slicing since your collection stores copies of the base type. Whenever you store something into the vector, your code just slices off the base part and it forgets its original type.
To fix this, you could store pointers to the base: std::vector<MWTypes*>, but then you have to manage your instances correctly to avoid memory leaks.
class RowSet
{
public:
// addElement assumes responsibility for the memory allocated for each 'elem'
void addElement(MWTypes* elem);
MWTypes* getElement();
std::vector<MWTypes*> getVector() { return m_row; }
// Destructor calls delete on every pointer in m_row
~RowSet();
private:
std::vector<MWTypes*> m_row;
};
Then you need to fix your code which calls addElement() to create new instances, and to get the long back again:
rowSet.getElement()->get();
You're problem lies with this function void addElememnt(MWTypes elem);. It should be either void addElememnt(MWTypes* elem); or void addElememnt(MWTypes& elem);. This is because by having an argument to be passed by-value, it loses it's polymorphism. The passing by-value calls the copy constructor of the base class and ONLY copies the contents of the base class (and the vtable) ignoring the rest from the derived class.
Also, if you need to store values of a certain base-class type, you need to consider using a list of pointers of the base-class type.
The problem lies here:
class RowSet
{
public:
void addElememnt(MWTypes elem);
You are taking elem by value, not by pointer or by reference, so the TypeLong subobject is sliced away, here: (reference: What Is The Slicing Problem in C++?)
TypeLong longObj(_long);
MWTypes *ptr = &longObj;
_long++;
rowSet.addElememnt(*ptr);
You need to change addElement to take a reference or a pointer.
Your vector, getElement, and addElememnt parts all invoke object slicing since they store the base object by value. You need to work with pointers or references in order to use runtime polymorphism.
In this case either a boost::ptr_vector or a vector of shared_ptr is probably what you want.
can I have this snippet of the code:
C *pa1 = new C(c2);
and I transfer it to another function:
foo(pa1);
what exactly do I transfer actual pointer or its copy, thanks in advance
and can somebody give some info about in which cases info is copied, and in which I transfer actual pointer
declaration of foo:
foo(A const *pa)
Assuming foo is declared as:
void foo(C* p);
you are passing a copy of the pointer.
This means, if foo does this:
p = &some_other_object;
that change to the pointer won't be seen by the caller.
It also means we're copying the pointer, not the thing pointed to. If foo does this:
p->bar = "Smurf!"
pa1 in the caller will also see the change. For this reason, pointers are often used to implement a kind of pass-by-reference.
If foo were declared:
void foo(C*& p);
then p would be a reference to pa1, and changes to p would result in changes to pa1. Historically, this has also been implemented using pointers to pointers:
void foo(C** p);
in which case you call foo like this:
foo(&pa1);
and foo can do something like:
*p = &some_other_object;
to change what pa1 points to.
Since pa1 is of type pointer-to-C, you are passing a pointer to C to the function foo. You are not copying the actual object.
To pass an object, you would need foo to take an object of type C and to dereference pa1 when passing it:
void foo(C);
...
foo(*pa1);
Since pa1 is a pointer (as you have defined it as a C *), you are passing a pointer to the C object into foo.
That said, whether you are passing the pointer by value or by reference is unknown without seeing the declaration of foo.
C *pa1;
object of type 'C*' is created on the stack (local variable pa1)
pa1 = new C(c2);
object of type 'C' is created on the heap, its address is returned into 'pa1' variable
foo(pa1)
We can say "object of type 'C*' (variable pa1) is passed by-value to function foo, the function gets a copy of the pointer",
But also "object of type 'C' (this one created on the heap) passed by-reference (through pointer) to function foo, no copy 'C' object is made.
To pass by-reference (without making a copy of an object) either c++ reference to the object can be used or pointer to the object. They act similar when going about function parameter passing, i.e all of 4 lines below gives the same effect, passing by-reference (these are const references, remove const keyword to be able to permanently modify the object by the foo function):
foo(const A *pa) { pa->DoSth(); } /*...*/ foo(some_pointer);
foo(const A *pa) { pa->DoSth(); } /*...*/ foo(&some_reference);
foo(const A &pa) { pa.DoSth(); } /*...*/ foo(*some_pointer);
foo(const A &pa) { pa.DoSth(); } /*...*/ foo(some_reference);