There's something I don't quite understand with how references are handled in C++:
B objB = B(); // Regular B object
const B &refConstObjB = objB; // Reference to const B object
B* ptrB = new B(); // Regular pointer to B
B* const &refPtrConstB = ptrB; // Reference to const pointer to B
All of the above compiles just fine. However the following doesn't:
const B* &refConstPtrB = ptrB; // Reference to pointer to const B
Considering both the object and the pointer were declared as non-const, why can I reference the object as a const object, but can't do the same with the pointer?
Just to note: A const reference does not imply a const object.
It simply means an object that is read-only through that reference. So whether or not an object is const, you can have a read-only reference or pointer to it.
Now, if what you mentioned were allowed, you could say refConstPtrB = pointer_to_some_const_B, and then mutate that object through ptrB, which would violate the const-ness of the pointer's target.
The reason for this error is that you can put a hole in the type system with it. Consider the following code:
const B dontChangeMe;
B* ptrB = new B();
/* This step is NOT legal. */
const B* &refConstPtrB = ptrB;
/* Uh oh! I now have ptrB pointing at dontChangeMe! */
refConstPtrB = &dontChangeMe;
Consequently, you can't do the assignment that you marked.
Hope this helps!
Related
I am trying to write a function that creates a class object from two pointers to other class objects, but I keep getting a read access violation error.
My program has three classes:
class A {
public:
A(const A& a2); // Special copy constructor
private:
int a = 0;
}
class B {
public:
B(const B& b2); // Special copy constructor
private:
int b = 0;
}
class C {
public:
A* aPtr;
B* bPtr;
}
What I am trying to do, is to have a function that takes a pointer to an A object and a B object, and then dereferences these into a C-instance. It is important that the objects themselves get copied since it is for multithreading purposes. The function is for example:
void foo(A* ptrA, B* ptrB){
C* ptrC = new C();
*ptrC->aPtr = *A;
*ptrC->bPtr = *B;
}
But this gives me a read access violation error.
What am I doing wrong? I know that objects themselves get copied without error, so I think it has something to do with how i declare ptrC or dereference ptrC->A and ptrC->B. Am I right about this?
What is a possible solution?
Thanks in advance.
A(const A& a2); // Special copy constructor
What you call a "special copy constructor" is commonly known as a converting constructor.
But this gives me a read access violation error. What am I doing
wrong?
C* ptrC = new C();
This creates a dynamic object and value initialises it. The pointers that are members are value initialised to null.
*ptrC->aPtr = *A;
*ptrC->bPtr = *B;
Here, you indirect through those null pointers and write to the memory. This results in undefined behaviour.
What is a possible solution?
Don't indirect through null pointers. Perhaps storing A and B objects instead of pointers within the C object would fit better to your design?
yes pretty much try:
void foo(A* ptrA, B* ptrB){
C* ptrC = new C();
ptrC->aPtr = ptrA;
ptrC->bPtr = ptrB;
}
instead
(and try to avoid pointers next time they never help you out with anything)
I have a doubt about downcasting an object in C++.
Here it comes an example:
class A { }
class B : public A {
public:
void SetVal(int i) { _v = i; }
private:
int _v;
}
A* a = new A();
B* b = dynamic_cast<B*>(a);
b->SetVal(2);
What would it happen with this example? We are modifying a base clase like if it is a child one... how does it work related with the memory?
With this cast... Is it like creating an instance of B and copying the values of A?
Thanks
A* a;
This just gives you a pointer to an A. It doesn't point anywhere in particular. It doesn't point at an A or B object at all. Whether your code works or not depends on the dynamic type of the object it is pointing at.
So there are two situations you might want to know about. First, this one:
A* a = new A();
B* b = dynamic_cast<B*>(a);
b->SetVal(2);
This will give you undefined behaviour because the dynamic_cast will return a null pointer. It returns a null pointer when the dynamic type of the object is really not a B. In this case, the object is an A. You then attempt to dereference the null pointer with b->SetVal(2), so you get undefined behaviour.
A* a = new B();
B* b = dynamic_cast<B*>(a);
b->SetVal(2);
This will work fine because the object really is a B object. The dynamic cast will succeed and the SetVal call will work just fine.
However, note that for this to work, A must be a polymorphic type. For that to be true, it must have at least one virtual member function.
That shouldn't even compile, because the classes aren't polymorphic so you can't use dynamic_cast.
If it did, it would be undefined behavior.
I have a function that takes a pointer as a reference argument, but I cannot pass in &my_variable to the function. The error I am receiving is cannot convert parameter from my_class* to my_class*&, using VS2010.
Why is this not allowed?
class my_class
{
public:
my_class();
my_class(my_class* &parent);
};
--
int main()
{
my_class a;
my_class b(&a); // Not legal
// ---
my_class a;
my_class* a_ptr = &a;
my_class b(a); // Legal
// ---
my_class* a = new my_class;
my_class* b = new my_class(a); // Legal
}
The result of an address-of expression is an rvalue. Therefore, you cannot bind it to reference-to-nonconst.
It also makes no sense. It's like saying int a; &a = 12; Obviously you cannot change the address of the variable a.
Instead, you want this:
int a;
int * p = &a;
mutate_me(p); // declared as mutate_me(int * &);
If the function does not need to mutate the pointer, pass it either by const-reference or by value.
Think about situation when you write something like
void foo(bar*& ptr) {
ptr = new bar;
}
bar b;
foo(&b);
Informally, a method expecting a parameter by reference expects that it gets passed something that can be legally placed on the left side of an assignment statement (sometimes called an "lvalue").
int main()
{
my_class a;
my_class b(&a); // Not legal: &a = 0; would be illegal because you can't change an address of a variable.
// ---
my_class a;
my_class* a_ptr = &a;
my_class b(a_ptr); // Legal: You've declared a_ptr on the stack and its value (what it points to) can be changed. The address of a_ptr would not be changeable though.
// ---
my_class* a = new my_class;
my_class* b = new my_class(a); // Legal: Again, this is on the stack and `a` could point to something else, but its own address won't be changed.
}
In this case, it's worth pointing out that in most cases, passing a pointer by value is inexpensive and will work. If you really need the pointer to be modifiable (passed by reference), then you need to pass an lvalue.
Another option is to have the reference be const. Then I believe you can pass rvalues just fine.
#include <cstdio>
using namespace std;
class A {
public:
virtual void func() { printf("A::func()"); }
};
class B : public A {
public:
virtual void func() { printf("B::func()"); }
};
int main() {
A a = *(A *)new B();
a.func();
}
The question is simple: why a->func() calls function in class A even though a contains object of class B?
A a = *(A *)new B();
a.func();
Here's what happens in this code, step by step:
new B(): a new object of type B is allocated on the free store, resulting in its address
(A*): the address of the object is cast to A*, so we have a pointer of type A* actually pointing to an object of type B, which is valid. All OK.
A a: here the problems start. A new local object of type A is created on the stack and constructed using the copy constructor A::A(const A&), with the first paremeter being the object created before.
The pointer to the original object of type B is lost after this statement, resulting in a memory leak, since it was allocated on the free store with new.
a.func() - the method is called on the (local) object of class A.
If you change the code to:
A& a = *( A*) new B();
a.func();
then only one object will be constructed, its pointer will be converted to pointer of type A*, then dereferenced and a new reference will be initialized with this address. The call of the virtual function will then be dynamically resolved to B::func().
But remember, that you'd still need to free the object since it was allocated with new:
delete &a;
Which, by the way, will only be correct if A has a virtual destructor, which is required that B::~B() (which luckily is empty here, but it doesn't need to in the general case) will also be called. If A doesn't have a virtual destructor, then you'd need to free it by:
delete (B*)&a;
If you would want to use a pointer, then that's the same as with the reference. Code:
A* a = new B(); // actually you don't need an explicit cast here.
a->func();
delete (B*)a; // or just delete a; if A has a virtual destructor.
Now that you've modified your code snippet, the problem is clear. Polymorphism (i.e. virtual functions) are only invoked via pointers and references. You have neither of these. A a = XXX does not contain an object of type B, it contains an object of type A. You've "sliced away" the B-ness of the object by doing that pointer cast and dereference.
If you do A *a = new B();, then you will get the expected behaviour.
The problem you encounter is classic object slicing :
A a = *(A *)new B();
Make a either a reference or pointer to A, and virtual dispatch will work as you expect. See this other question for more explanations.
You commented on another answer that "Compiler should at least give warning or what". This is why is it considered a good practice to make base classes either abstract of non copyable : your initial code wouldn't have compiled in the first place.
This might do that trick.
A &a = *(A *)new B();
a.func();
Or
A *a = new B();
a->func();
Virtual dispatch works only with pointer or reference types:
#include <cstdio>
using namespace std;
class A {
public:
virtual void func() { printf("A::func()"); }
};
class B : public A {
public:
virtual void func() { printf("B::func()"); }
};
int main() {
A* a = new B();
a->func();
}
The problem is the deference and casting of B to A with the A a = *(A *)new B();
You can fix it with just removing the *(A *) changing it to (A *a = new B(); ) but I would take it a step further since your variable name is not good for instantiation of B.
It should be
B *b = new B();
b->func();
Because you performed slicing when you copied the dynamically allocated object into object a of type A (which also gave you a memory leak).
a should be a reference (A&) instead, or just keep the pointer.
In C++ a stack-allocated object can be declared const:
const Class object;
after that trying to call a non-const method on such object is undefined behaviour:
const_cast<Class*>( &object )->NonConstMethod(); //UB
Can a heap-allocated object be const with the same consequences? I mean is it possible that the following:
const Class* object = new Class();
const_cast<Class*>( object )->NonConstMethod(); // can this be UB?
is also undefined behaviour?
Yes. It's legal to construct and destroy a const heap object. As with other const objects, the results of manipulating it as a non-const object (e.g. through a const_cast of a pointer or reference) causes undefined behaviour.
struct C
{
C();
~C();
};
int main()
{
const C* const p = new const C;
C* const q = const_cast<C*>(p); // OK, but writes through q cause UB
// ...
delete p; // valid, it doesn't matter that p and *p are const
return 0;
}
In your heap example, new returns a pointer to non-const. The fact that you've stored it in a pointer to const (and then const_casted it back to a pointer to non-const) doesn't change the fact that the object itself is not const in the same way as the stack-allocated one is.
However, you can create a const object on the heap:
const Class* object = new const Class();
In such a case, casting to a pointer to non-const and calling a non-const method would be the same situation as the const stack-allocated object.
(The idea of creating a const object on the heap was new to me, I had never seen that before. Thanks to Charles Bailey.)
Yes, a heap-allocated object can be const. Consider this excerpt from the example in 7.1.5.1/5:
const int* ciq = new const int (3); // initialized as required
int* iq = const_cast<int*>(ciq); // cast required
*iq = 4; // undefined: modifies a const object
The example you gave in the question is fine because you're not asking new to make a const object; you're just storing the result in a pointer-to-const.
Don't forget mutable members
It won't be undefinied behaviour if the NonConstMethod only modifies mutable qualified members (see 7.1.5.1 (4)) of a const qualified class. Yes, otherwise it's undefined behaviour.
const A* p = new(const A);
A *q = const_cast<A*>(p);
q->NonConstMethodThatModifiesMembers(); // undefined behaviour!
q->NonConstMethodThatOnlyModifiesMutableMembers(); // defined behaviour!
Obviously:
struct Foo {
const int Bar;
Foo() : Bar(42) { }
};
Foo* foo = new Foo;
const_cast<int&>(foo->Bar); // don't do this.
const_cast can cause UB when the object is actually read-only (for example, the compiler can create such objects when you use hard coded strings in your code, by placing them in certain memory areas that are read only) for some reason. This will not happen with heap allocated objects, no matter how you keep their reference (const pointer, const reference, whatever).