In relation to question "reference to abstract class" I wrote the following example:
#include <iostream>
#include <vector>
class data
{
public:
virtual int get_value() = 0;
};
class data_impl : public data
{
public:
data_impl( int value_ ) : value( value_ ) {}
virtual int get_value() { return value; }
private:
int value;
};
class database
{
public:
data& get( int index ) { return *v[index]; }
void add( data* d ) { v.push_back( d ); }
private:
std::vector< data* > v;
};
int main()
{
data_impl d1( 3 );
data_impl d2( 7 );
database db;
db.add( &d1 );
db.add( &d2 );
data& d = db.get( 0 );
std::cout << d.get_value() << std::endl;
d = db.get( 1 );
std::cout << d.get_value() << std::endl;
data& d_ = db.get( 1 );
std::cout << d_.get_value() << std::endl;
d_ = db.get( 0 );
std::cout << d_.get_value() << std::endl;
return 0;
}
To my surprise, the example prints:
3
3
7
7
and it looks like the reference assignment does the work differently from my expectation. I would expect:
3
7
7
3
Could you please point what my mistake is?
Thank you!
In
data& d = db.get( 0 );
std::cout << d.get_value() << std::endl;
d = db.get( 1 );
std::cout << d.get_value() << std::endl;
the first statement is a reference initialization, while the third statement is a slicing assignment.
You can not re-seat a reference.
Cheers & hth.,
The reason is, references can't be reassigned.
You assigned the reference d to the first element in db:
data& d = db.get( 0 );
Then later you tried to reassign it:
d = db.get( 1 );
However, this doesn't change the reference itself, instead it changes the value the reference points to.
However, in this case the reference is to an abstract base class which contains no data. So the assignment isn't changing anything.
In effect you print the first element in db twice, then the 2nd element again twice.
I am going to simplify the example a bit:
data_impl a(3), b(7);
data &ra(a);
data &rb(b);
std::cout << ra.get_value() << std::endl;
ra = rb; // [1]
std::cout << ra.get_value() << std::endl;
Now with that simplified code it is easier to reason about the program. You obtain a reference ra to the a subobject, but the reference is of type data, rather than data_impl. Similarly with rb and b. In the line marked with [1] you are performing an assignment from rb to ra, the static type of the two arguments of the expression is data, and that means that regardless of what the real object they refer to is, that particular line is assigning only the data subobject. This is called slicing.
That is, [1] is setting the data subobject of a to be the same as the data subobject of b. Because data does not contain any actual data, the result is that a remains unmodified.
For a more illustrative example, you can add a field to data, and check that the expression does modify that field in a, even if it does not modified the fields in derived classes.
Then you can try an implement operator= manually as a virtual function and check that you can get the expected result, but the implementation will be a bit messier, as there are no covariant arguments to member functions in C++, which means that the signature of operator= at all levels will take a (const) reference to data, and you will be forced to downcast (verify that the cast succeeded) and then perform the assignment...
The problem can be seen here:
data& d = /**/;
d = /**/; <---
Here, the static type of d is data&, meaning that it is in fact syntactic sugar for:
d.operator=(/**/);
Since data::operator= is not virtual, then it is called. No dispatch to a derived class. And since data is a pure interface, it does not have any attribute, so the code is meaningless.
The problem is, there are no real solution here:
Making operator= virtual is a mess, because the parameter must be a data const& in data, and cannot later be changed in subsequent derived classes, meaning a loss of type safety
Using a setValue/getValue virtual pair will only work if the derived classes do not hold more data than they present via this interface, which is an unreasonnable assumption
Since the problem cannot be solved, the only way out is to make diagnosis easier: disabling copy and assignment in the base class.
There are two ways to do this:
Make the whole hierarch non-copyable (either inherit from boost::noncopyable or delete the copy operator and copy assignment operator, etc...)
Make the current class non-copyable, but allow derived classes to be copyable automatically, by making the copy constructor and assignment operator protected.
In general, it is an error for a non-final class to have a public copy constructor and assignment operator (I wonder if any compiler diagnose this).
Following lines might not be what you think:
data& d = db.get( 0 );
std::cout << d.get_value() << std::endl;
d = db.get( 1 );
the third line, d does not become a reference to the second database entry. It is still a reference to the first. References can only be initialized on creation.
You can't separate the reference from the referent. enter link description here
Unlike a pointer, once a reference is bound to an object, it can not be "reseated" to another object. The reference itself isn't an object (it has no identity; taking the address of a reference gives you the address of the referent; remember: the reference is its referent).
The strangest thing is the compiler don't warn it.
I try with gcc -Wall -pedantic
Related
I am struggling to understand the difference in behaviour of a raw pointer and a unique_ptr. I have class A with a variable x and class B with a pointer to an instance of A:
class A
{
public:
int x;
};
A::A(int y) : x(y)
{
}
class B
{
public:
B(A &);
A *p;
};
B::B(A &a)
{
p = &a;
}
This behaves as I would expect:
int main()
{
A a(2);
B b(a);
cout << a.x << " " << b.p->x << endl;
a.x = 4;
cout << a.x << " " << b.p->x << endl;
}
gives
2 2
4 4
Changing the raw pointer to a std::unique_ptr gives a different result:
class A
{
public:
int x;
};
A::A(int y) : x(y)
{
}
class B
{
public:
B(A &);
std::unique_ptr<A> p;
};
B::B(A &a)
{
p = std::make_unique<A>(a);
}
gives
2 2
4 2
Have I fundamentally misunderstood something about unique_ptrs?
make_unique creates a fresh object, one that that unique_pt has exclusive access to. So in the second example you have two objects, not one and when you set change the value of a.x in the first object it doesn't effect the other object held by the unique_ptr.
A unique pointer needs to own whatever it points to. Your code can be made to work - just substituting unique_ptr type and leaving everything else unchanged (no make_unique). But it will have undefined behavior, since you’ll create a unique pointer to an object that is owned elsewhere.
To compare apples to apples, the raw pointer code should read p=new A(a);. That’s what make_unique does.
Try reading the following expression from the smart pointer version:
std::make_unique<A>(a);
"make a unique A from a" (no mention of pointers!)
The result is a unique_ptr, but when reading the expression, read it as making (an object whose type is) the template parameter. The function parameters are parameters to the constructor. In this case, you are making an A object from an A object, which pulls in the copy constructor.
Once you understand that the smart pointer version is making a new A object (and your raw pointer version does not), your results should make sense.
The "unique" in "unique A" might be tricky to understand. Think of it as an object that no one else can lay claim to. It might be a copy of another object, but, taking the role of the unique_ptr, it is your copy, your responsibility to clean up after, and no one else's. Your preciousss, which you will not share (c.f. std::make_shared).
Note that a local variable (like the a in the main function) is the responsibility of the compiler, so it is ineligible to be the object to which a unique_ptr points (or any smart pointer, for that matter).
What is the difference between value cast and reference cast? Why one of them invokes conversion (aka creating new object) and other doesn't? What are caveats of using casting on rhs?
Assume this is Derived . Why those will not actually cast to Base?
*this = (Base&) rhs
(Base)* this = rhs
Could you please show on simple examples?
Value cast creates a new value from an existing one; reference cast creates a new reference to the same existing value.
Reference cast neither changes the content of an existing object nor creates a new one; it is restricted to changing the interpretation of the value that is already there. Value casting, on the other hand, can make a new object from an existing one, so it has fewer restrictions.
For example, if you have an unsigned char and you want a value or a reference of type int, value cast is going to work, while reference casting is going to fail:
unsigned char orig = 'x';
int v(orig); // Works
int &r(orig); // Does not work
rhs is Derived, I want to assign all inherited and non-inherited stuff from rhs into Base
Then you need to cast both sides to Base, or add an assignment operator to Derived that takes a const Base& as an argument. Here is an example of casting on both sides (may be hard to understand by other programmers reading your code)
struct Base {
int x;
Base(int x) : x(x) {}
};
struct Derived1 : public Base {
Derived1(int x) : Base(x) {}
};
struct Derived2 : public Base {
Derived2(int x) : Base(x) {}
};
Running the code below
Derived1 d1(5);
Derived2 d2(10);
cout << d1.x << " " << d2.x << endl;
((Base&)d1) = (Base&)d2;
cout << d1.x << " " << d2.x << endl;
produces the following printout:
5 10
10 10
As you can see, the assignment ((Base&)d1) = (Base&)d2 copied the content of d2's Base portion into d1's Base portion (demo).
What is the difference between value cast and reference cast?
Value casts convert an object to the value:
char i = 'a';
int k = static_cast<int>(i); // Prefer C++ casts to C casts
Reference casts convert an object to a reference:
char i = 'a';
int &k = static_cast<int&>(i);
Just because the conversion can be done implicitly in int &k = i doesn't mean it doesn't happen.
Why one of them invokes conversion (aka creating new object) and other doesn't?
If you write int &x = static_cast<int&>(i), there are 2 things that can happen:
1) A pointer is created pointing to i (references are hidden pointers). Then this hidden pointer gets assigned to x, and x behaves as a reference of i.
2) Usually, the compiler optimizes away this reference, and simply considers x an alias of i. Therefore no variable is instantiated.
In the former case, a new object is created.
However, if you write:
char c = 'a';
int i = static_cast<int> (c);
there is no instantiation, just a copy of the memory from c to i.
Why those will not actually cast to Base?
*this = (Base&) rhs
You cannot assign the base object to a derived object, only the opposite. This will most likely overwrite the fields of the base object to the derived object's.
(Base)* this = rhs
There is no point in castling an l-value. This is equivalent to:
*this = rhs;
What are caveats of using casting on rhs?
I don't think there is anything wrong with casting, as long as they do not decrease readability.
I have written the following sample code:
#include <iostream>
class B
{
int Value;
public:
B(int V) : Value(V) {}
int GetValue(void) const { return Value;}
};
class A
{
const B& b;
public:
A(const B &ObjectB) : b(ObjectB) {}
int GetValue(void) { return b.GetValue();}
};
B b(5);
A a1(B(5));
A a2(b);
A a3(B(3));
int main(void)
{
std::cout << a1.GetValue() << std::endl;
std::cout << a2.GetValue() << std::endl;
std::cout << a3.GetValue() << std::endl;
return 0;
}
Compiled with mingw-g++ and executed on Windows 7, I get
6829289
5
1875385008
So, what I get from the output is that the two anonymous object are destroyed as the initialization has completed, even if they are declared in a global context.
From this my question: does is exist a way to be sure that a const reference stored in class will always refer to a valid object?
One thing you can do in class A:
A(B&&) = delete;
That way, the two lines that try to construct an A from a B temporary will fail to compile.
That obviously won't prevent you providing some other B object with a lifetime shorter than the A object referencing it, but it's a step in the right direction and may catch some accidental/careless abuses. (Other answers already discuss design scenarios and alternatives - I won't cover that same ground / whether references can be safe(r) in the illustrated scenario is already an important question.)
No, there is not. Remember that references are pointers under the hood, and normally don't control the lifetime of the object they reference (see here for an exception, although it doesn't apply in this case). I would recommend just having a B object, if this is in a piece of code that you need.
Also, you could utilize an object such as a shared_ptr in C++11, which will only eliminate the object once both the pointer in the function and in the object have been destroyed.
I found this example interview question and would like some help understanding it:
#include <iostream>
class A
{
public:
A(int n = 0)
: m_n(n)
{
++m_ctor1_calls;
}
A(const A& a)
: m_n(a.m_n)
{
++m_copy_ctor_calls;
}
public:
static int m_ctor1_calls;
static int m_copy_ctor_calls;
private:
int m_n;
};
int A::m_ctor1_calls = 0;
int A::m_copy_ctor_calls = 0;
void f(const A &a1, const A &a2 = A())
{
}
int main()
{
A a(2), b = 5;
const A c(a), &d = c, e = b;
std::cout << A::m_ctor1_calls << A::m_copy_ctor_calls;
b = d;
A *p = new A(c), *q = &a;
std::cout << A::m_copy_ctor_calls;
delete p;
f(3);
std::cout << A::m_ctor1_calls << A::m_copy_ctor_calls << std::endl;
return 0;
}
The way I understand it, the first line of main creates two new objects, resulting in 2 calls to the constructor. In the second line, I see that they use the copy constructor for c(a) and e = b. The copy constructor isn't used for &d = c because it is only referencing c is that right? Also one thing I don't understand is that if the copy constructor requires a reference, how come an object is being passed into it instead of a reference to the object? The parts after with pointers are really confusing to me. Can someone provide some insight?
Thanks!
The copy constructor isn't used for &d = c because it is only
referencing c is that right?
Yes. d becomes an alias for c.
Also one thing I don't understand is that if the copy constructor
requires a reference, how come an object is being passed into it
instead of a reference to the object?
An object passed into a function taking a reference is automatically "converted" into a reference. The parameter is now an alias for the passed-in object.
The parts after with pointers are really confusing to me. Can someone
provide some insight?
p points to a newly allocated A object, copied from c. q points to a. (1 copy constructor). Then p is deleted.
f(3) gets fun. It constructs a temporary A initialized with 3 to bind to a1. a2 gets a temporary default constructed A. After f(3) finishes, these two temporaries are destroyed.
End of the function, and the remaining instances of A are destroyed.
Outside of an interview, you can stick this code in an IDE and step through it with a debugger.
In case you were wondering, here is the output (with spaces added):
2 2 3 4 3
Recently, I found an interesting discussion on how to allow read-only access to private members without obfuscating the design with multiple getters, and one of the suggestions was to do it this way:
#include <iostream>
class A {
public:
A() : _ro_val(_val) {}
void doSomething(int some_val) {
_val = 10*some_val;
}
const int& _ro_val;
private:
int _val;
};
int main() {
A a_instance;
std::cout << a_instance._ro_val << std::endl;
a_instance.doSomething(13);
std::cout << a_instance._ro_val << std::endl;
}
Output:
$ ./a.out
0
130
GotW#66 clearly states that object's lifetime starts
when its constructor completes successfully and returns normally. That is, control reaches the end of the constructor body or an earlier return statement.
If so, we have no guarantee that the _val memeber will have been properly created by the time we execute _ro_val(_val). So how come the above code works? Is it undefined behaviour? Or are primitive types granted some exception to the object's lifetime?
Can anyone point me to some reference which would explain those things?
Before the constructor is called an appropriate amount of memory is reserved for the object on Freestore(if you use new) or on stack if you create object on local storage. This implies that the memory for _val is already allocated by the time you refer it in Member initializer list, Only that this memory is not properly initialized as of yet.
_ro_val(_val)
Makes the reference member _ro_val refer to the memory allocated for _val, which might actually contain anything at this point of time.
There is still an Undefined Behavior in your program because, You should explicitly initialize _val to 0(or some value,you choose)in the constructor body/Member Initializer List.The output 0 in this case is just because you are lucky it might give you some other values since _val is left unInitialized. See the behavior here on gcc 4.3.4 which demonstrates the UB.
But as for the Question, Yes indeed the behavior is Well-Defined.
The object's address does not change.
I.e. it's well-defined.
However, the technique shown is just premature optimization. You don't save programmers' time. And with modern compiler you don't save execution time or machine code size. But you do make the objects un-assignable.
Cheers & hth.,
In my opinion, it is legal (well-defined) to initialize a reference with an uninitialized object. That is legal but standard (well, the latest C++11 draft, paragraph 8.5.3.3) recommends using a valid (fully constructed) object as an initializer:
A reference shall be initialized to refer to a valid object or function.
The next sentence from the same paragraph throws a bit more light at the reference creation:
[Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” obtained by dereferencing a null pointer, which causes undefined behavior.]
I understand that reference creation means binding reference to an object obtained by dereferencing its pointer and that probably explains that the minimal prerequisite for initialization of reference of type T& is having an address of the portion of the memory reserved for the object of type T (reserved, but not yet initialized).
Accessing uninitialized object through its reference can be dangerous.
I wrote a simple test application that demonstrates reference initialization with uninitialized object and consequences of accessing that object through it:
class C
{
public:
int _n;
C() : _n(123)
{
std::cout << "C::C(): _n = " << _n << " ...and blowing up now!" << std::endl;
throw 1;
}
};
class B
{
public:
// pC1- address of the reference is the address of the object it refers
// pC2- address of the object
B(const C* pC1, const C* pC2)
{
std::cout << "B::B(): &_ro_c = " << pC1 << "\n\t&_c = " << pC2 << "\n\t&_ro_c->_n = " << pC1->_n << "\n\t&_c->_n = " << pC2->_n << std::endl;
}
};
class A
{
const C& _ro_c;
B _b;
C _c;
public:
// Initializer list: members are initialized in the order how they are
// declared in class
//
// Initializes reference to _c
//
// Fully constructs object _b; its c-tor accesses uninitialized object
// _c through its reference and its pointer (valid but dangerous!)
//
// construction of _c fails!
A() : _ro_c(_c), _b(&_ro_c, &_c), _c()
{
// never executed
std::cout << "A::A()" << std::endl;
}
};
int main()
{
try
{
A a;
}
catch(...)
{
std::cout << "Failed to create object of type A" << std::endl;
}
return 0;
}
Output:
B::B(): &_ro_c = 001EFD70
&_c = 001EFD70
&_ro_c->_n = -858993460
&_c->_n = -858993460
C::C(): _n = 123 ...and blowing up now!
Failed to create object of type A