If I understand slicing correctly I don't think this could happen with pointers or smart pointers. For example, if you had:
class A
{
int something;
};
class B : public A
{
int stuff;
int morestuff;
};
int main()
{
std::shared_ptr<B> b(new B());
std::shared_ptr<A> a;
a = b;
}
My understanding is that the block of memory allocated to the object pointed to by "b" is still the same and doesn't change when assigned to the smart pointer "a".
Please confirm or reject my understanding, or let me know of any pitfalls associated with this.
A smart pointer is still a pointer, so such an assignment won't cause slicing. Slicing happens only when dealing with values, not pointers. Note, however, templates don't know about the relationships between the items the point at, so even though B derives from A, shared_pointer<B> doesn't derived from shared_pointer<A>, so an assignment doesn't (automatically) get an automatic up-cast like it would with native pointers.
Edit: elaborating on final point.
Slicing happens with values, not pointers, so (given your definitions of A and B), something like:
A ax = b;
would work, but would "slice" the B object to become an A object. If, however, you have some sort of template that holds an instance of the item:
template <class T>
class holder {
T t_;
public:
holder &operator=(T const &t) {
t_ = t;
return *this;
}
holder &operator=(holder const &t) { t_ = t; return *this; }
};
Now, if we try to assign one value to another, like would cause slicing:
holder<A> ha;
holder<B> hb;
A a;
B b;
ha = a;
hb = b;
ha = hb;
we will NOT get slicing. Instead, the compiler will simply give us an error, telling us that holder<A> and holder<B> are not related types, so the assignment can't happen -- without adding an explicit cast, it simply won't compile.
You're correct, but they're not the same: you can't evaluate a->stuff.
Related
I have a question regarding the assignment operator (apologies if this has already been answered in a different post).
As I understand the assignment operator, it is suppose to assign the value of one object to another, e.g.
class A {
public:
A();
A& operator=(const A& rhs)
{
b = rhs.b;
return *this;
}
private:
B b;
};
Now, if the class contains pointers, it seems to be common practice to allocate new memory in the assignment operator. This makes the assignment operator more complicated since one needs to be more careful, e.g.
class A {
public:
A() : b(0)
{
b = new B;
}
A& operator=(const A& rhs)
{
if (this != &rhs) {
B* b1 = 0;
try {
b1 = new B(*rhs.b);
}
catch {
delete b1;
throw;
}
delete b;
b = b1;
}
return *this;
}
private:
B* b;
};
So my question is: Why not assign the value of the existing object pointed to, i.e. reuse the memory by calling B's assignment operator as one would do if b was not a pointer (see my first example)? Then the assignment would look like this
class A {
public:
A() : b(0)
{
b = new B;
}
A& operator=(const A& rhs)
{
*b = *rhs.b;
return *this;
}
private:
B* b;
};
This is much more simple and does exactly what I would expect from the assignment operator.
Moreover, I can think of at least one example where the more complicated assignment operator will get me into trouble: If class A has a member function which returns b
const B* A::GetB() const
{
return b;
}
then the following code will leave a dangling pointer
A a1, a2;
const B* my_b = a1.GetB();
a1 = a2; // this leaves my_b dangling!
Your comments are much appreciated. Thanks!
You should always reuse existing memory whenever possible. In you case, you should reuse the existing memory only if you are dead certain that b points to valid memory.
It depends on the exact scenario at hand really. Usually, it is a good practice to reuse the memory, but really, when you are trying to assign a differently sized internal array, etc, you might be better off with reallocating or delete/new.
Note that, the own-cope check at the beginning would still be desired to avoid A = A alike situation, however.
I can't think of any reason that you shouldn't try to do that. The examples that I have found where the memory is deleted is due to the fact that the type itself wasn't copyable. If you have a class attribute and it is a pointer to a copyable type then your suggestion should work conceptually (meaning that you only posted a partial snip so I am not saying that what is in your example is perfect). Some older examples show how it is done with char* and int* or other types of dynamic arrays. However, modern C++ programmers should be trying to use std::string and container classes. Now we also have smart pointers so that you can have shared pointers without having to worry about dangling references or worrying about which object is really the owner of it. Below is an example of what I mean. The example does use a template but copies as though the T is a c-array (not a container class) so deep copying has to be done as in your first example.
http://www.cplusplus.com/articles/y8hv0pDG/
I have't coded in c++ for some time and I got stuck when I tried to compile this simple snippet:
class A
{
public:
void f() {}
};
int main()
{
{
A a;
a.f(); // works fine
}
{
A *a = new A();
a.f(); // this doesn't
}
}
It's a pointer, so instead try:
a->f();
Basically the operator . (used to access an object's fields and methods) is used on objects and references, so:
A a;
a.f();
A& ref = a;
ref.f();
If you have a pointer type, you have to dereference it first to obtain a reference:
A* ptr = new A();
(*ptr).f();
ptr->f();
The a->b notation is usually just a shorthand for (*a).b.
A note on smart pointers
The operator-> can be overloaded, which is notably used by smart pointers. When you're using smart pointers, then you also use -> to refer to the pointed object:
auto ptr = make_unique<A>();
ptr->f();
Allow an analysis.
#include <iostream> // not #include "iostream"
using namespace std; // in this case okay, but never do that in header files
class A
{
public:
void f() { cout<<"f()\n"; }
};
int main()
{
/*
// A a; //this works
A *a = new A(); //this doesn't
a.f(); // "f has not been declared"
*/ // below
// system("pause"); <-- Don't do this. It is non-portable code. I guess your
// teacher told you this?
// Better: In your IDE there is prolly an option somewhere
// to not close the terminal/console-window.
// If you compile on a CLI, it is not needed at all.
}
As a general advice:
0) Prefer automatic variables
int a;
MyClass myInstance;
std::vector<int> myIntVector;
1) If you need data sharing on big objects down
the call hierarchy, prefer references:
void foo (std::vector<int> const &input) {...}
void bar () {
std::vector<int> something;
...
foo (something);
}
2) If you need data sharing up the call hierarchy, prefer smart-pointers
that automatically manage deletion and reference counting.
3) If you need an array, use std::vector<> instead in most cases.
std::vector<> is ought to be the one default container.
4) I've yet to find a good reason for blank pointers.
-> Hard to get right exception safe
class Foo {
Foo () : a(new int[512]), b(new int[512]) {}
~Foo() {
delete [] b;
delete [] a;
}
};
-> if the second new[] fails, Foo leaks memory, because the
destructor is never called. Avoid this easily by using
one of the standard containers, like std::vector, or
smart-pointers.
As a rule of thumb: If you need to manage memory on your own, there is generally a superiour manager or alternative available already, one that follows the RAII principle.
Summary: Instead of a.f(); it should be a->f();
In main you have defined a as a pointer to object of A, so you can access functions using the -> operator.
An alternate, but less readable way is (*a).f()
a.f() could have been used to access f(), if a was declared as:
A a;
a is a pointer. You need to use->, not .
The questions I have are NOT homework questions, but I am considering using these concepts in my assignment. The context, if it helps, is like this: I need to keep track of several union instances and they belong to my own union in one of my own classes as class variables. (Note: the number of union instances is unknown, so I cannot just have a fixed number of union instances.
Q1: If I have a union, say MyUnion,
and many instances of this union,
can I then put them into a vector
like
vector<union MyUnion> myVector(10);
Q2: Is it valid to have a pointer of
union? Like
union MyUnion *myUnionPtr = new union myUnion;
Q3: I am considering using a vector
of union pointers in my
implementation, is this concept
correct? Also, is it a normal
approach in C++? Do I need to
rethink about my design?
If the union is CopyConstructable and Assignable, then it meets the requirements for std::vector.
Yes: MyUnion* ptr = new MyUnion();
A container of pointers works in some situations, but if you want a container of owning pointers, look at Boost's ptr_* containers. However, here it appears you'd either have a container of non-pointers or a container of non-owning pointers, either of which is fine and common for vector.
All types are, by default, both CopyConstructable and Assignable. ("Copyable" is used to mean the union of these concepts, however the standard specifies them individually.) This is because copy ctors and op= are added to classes (unions are one class-type) except under certain circumstances. There are a few references online, but I'm not aware of a single one, available freely online, that spells these out.
You have to go out of your way to prevent it, such as:
make the copy ctor or op= non-public
make the copy ctor or op= take a non-const reference
give the class type a non-CopyConstructable or non-Assignable member
Example:
union CopyableUnion {
int n;
char c;
double d;
};
union NonCopyableUnion {
int n;
char c;
double d;
NonCopyableUnion() {} // required, because any user-defined ctor,
// such as the private copy ctor below, prevents the supplied
// default ctor
private:
NonCopyableUnion(NonCopyableUnion const&);
NonCopyableUnion& operator=(NonCopyableUnion const&);
};
int main() {
CopyableUnion a;
CopyableUnion b = a; // fine, uses copy ctor
b = a; // fine, uses op=
NonCopyableUnion c;
NonCopyableUnion d = c; // compile error (copy ctor)
d = c; // compile error (op=)
return 0;
}
Note: And just because something is Copyable, doesn't mean it does what you want! Example:
struct A {
int* p;
A() : p(new int()) {}
// the provided copy ctor does this:
//A(A const& other) : p(other.p) {}
// which is known as "member-wise" copying
~A() { delete p; }
};
int main() {
A a;
{
A b = a;
assert(b.p == a.p); // this is a problem!
} // because when 'b' is destroyed, it deletes the same pointer
// as 'a' holds
return 0; // and now you have Undefined Behavior when
// ~A tries to delete it again
}
The same thing holds true for unions, of course. The fix applies equally, though, as well:
struct A {
int* p;
A() : p(new int()) {}
A(A const& other) : p(new int(*other.p)) {}
~A() { delete p; }
};
(If you spotted it, yes, A has a problem if you ever try to use op=, in just the same way as it originally had with the copy ctor.)
I have the following contrived example (coming from real code):
template <class T>
class Base {
public:
Base(int a):x(a) {}
Base(Base<T> * &other) { }
virtual ~Base() {}
private:
int x;
};
template <class T>
class Derived:public Base<T>{
public:
Derived(int x):Base<T>(x) {}
Derived(Derived<T>* &other): Base<T>(other) {}
};
int main() {
Derived<int> *x=new Derived<int>(1);
Derived<int> y(x);
}
When I try to compile this, I get:
1X.cc: In constructor ‘Derived<T>::Derived(Derived<T>*&) [with T = int]’:
1X.cc:27: instantiated from here
1X.cc:20: error: invalid conversion from ‘Derived<int>*’ to ‘int’
1X.cc:20: error: initializing argument 1 of ‘Base<T>::Base(int) [with T = int]’
1) Clearly gcc is being confused by the constructors. If I remove the reference
from the constructors, then the code compiles. So my assumption is that something goes wrong
with up-casting pointer references. Can someone tell me what is going on here?
2) A slightly unrelated question. If I were to do something horrendous like "delete other" in the constructor (bear with me),
what happens when someone passes me a pointer to something on the stack ?
E.g. Derived<int> x(2);
Derived<int> y(x);
where
Derived(Derived<T>*& other) { delete other;}
How can I make sure that pointer is legitimately pointing to something on the heap?
Base<T> is a base type of Derived<T>, but Base<T>* is not a base type of Derived<T>*. You can pass a derived pointer in place of a base pointer, but you can't pass a derived pointer reference in place of a base pointer reference.
The reason is that, suppose you could, and suppose the constructor of Base were to write some value into the reference:
Base(Base<T> * &other) {
Base<T> *thing = new Base<T>(12);
other = thing;
}
You've just written a pointer to something which is not a Derived<T>, into a pointer to Derived<T>. The compiler can't let this happen.
You cannot convert a reference to a pointer to Derived to a reference to a pointer to Base. (Templates don't contribute to the issue here, so removed from my example below.)
If you want to defer responsibility for a pointer, use a smart pointer type. Smart pointer types can represent the "responsibility to delete" that raw pointers cannot. Examples include std::auto_ptr and boost::shared_ptr, among many others.
Why you cannot upcast pointer references:
struct Base {};
struct Derived : Base {};
struct Subclass : Base {};
int main() {
Derived d;
Derived* p = &d;
Derived*& d_ptr = p;
Base*& b_ptr = d_ptr; // this is not allowed, but let's say it is
Base b;
b_ptr = &b; // oops! d_ptr no longer points to a Derived!
Subclass s;
b_ptr = &s; // oops! d_ptr no longer points to a Derived!
}
When you pass your 'other' parameter to the Base ctor, you're trying to do the same thing as b_ptr = d_ptr above.
You make sure that pointer points to something on the heap by writing that in your documentation and relying on the caller to abide by that. If whoever calls your constructor passes a stack pointer, all bets are off, and it's not your fault - you can try to catch the problem early, but no guarantees.
That's how the standard library works - often it'll catch obvious errors, but it's not required to, and it's up to the caller to make sure they're not doing anything stupid.
Your x variable is not a pointer, it should be if you want to assign a new Derived<int> to it.
As for deleting things on the stack, don't do it. There is no way to tell whether you have been passed the address of something on the stack or on the heap (indeed, the C++ standard doesn't even acknowledge the existence of a stack). The lesson here is that you shouldn't be deleting things that you don't own, especially if you have no way of telling where they came from.
Not sure why do you want reference to the pointer. Why not
Base(Base<T> * other) { }
and
Derived(Derived<T>* other): Base<T>(other) {}
That should work.
And, like other answered, I don't think you can legitimately know whether the pointer is pointing into heap.
Edit: why can't one do what you're trying to: consider example:
Derived1<int> *x = new Derived1<int>
Base<int> **xx =&x;
Derived2<int> y;
*xx = &y;
Where Derived1 and Derived2 are different classes derived from Base? Would you think it's legitimate? Now that x of type Derived1* points to Derived2?
#include "iostream"
class A {
private:
int a;
public :
A(): a(-1) {}
int getA() {
return a;
}
};
class A;
class B : public A {
private:
int b;
public:
B() : b(-1) {}
int getB() {
return b;
}
};
int main() {
std::auto_ptr<A> a = new A();
std::auto_ptr<B> b = dynamic_cast<std::auto_ptr<B> > (a);
return 0;
}
ERROR: cannot dynamic_cast `(&a)->std::auto_ptr<_Tp>::get() const
Well, std::auto_ptr<B> is not derived from std::auto_ptr<A>. But B is derived from A. The auto_ptr does not know about that (it's not that clever). Looks like you want to use a shared ownership pointer. boost::shared_ptr is ideal, it also provides a dynamic_pointer_cast:
boost::shared_ptr<A> a = new A();
boost::shared_ptr<B> b = dynamic_pointer_cast<B> (a);
For auto_ptr, such a thing can't really work. Because ownership will move to b. But if the cast fails, b can't get ownership. It's not clear what to do then to me. You would probably have to say if the cast fails, a will keep having the ownership - which sounds like it will cause serious trouble. Best start using shared_ptr. Both a and b then would point to the same object - but B as a shared_ptr<B> and a as a shared_ptr<A>
dynamic cast doesn't work that way. A : public B does not imply auto_ptr<A> : public auto_ptr<B>. This is why boost's shared_ptr provides shared_dynamic_cast. You could write an auto_ptr dynamic cast though:
template<typename R, typename T>
std::auto_ptr<R> auto_ptr_dynamic_cast(std::auto_ptr<T>& in) {
auto_ptr<R> rv;
R* p;
if( p = dynamic_cast<R*>( in.get() ) ) {
in.release();
rv = p;
}
return rv;
}
Just be aware of what happens here. Since auto_ptrs have ownership semantics, a successful downcast means the original more generally typed, auto_ptr no longer has ownership.
The reason is that auto_ptr is not actually a pointer. It's a smart pointer which is a pointer wrapper but not actually a pointer. The type that is passed as a template style argument to dynamic_cast must be a true pointer (or reference) type.
http://msdn.microsoft.com/en-us/library/cby9kycs(VS.80).aspx
You're trying to cast a A* (returned by a.get()) to std::auto_ptr<B>, and since the second is not even a pointer type this fails. Probably you just want to cast it to B*:
std::auto_ptr<A> a(new A());
std::auto_ptr<B> b(dynamic_cast<B*>(a.get()));
This will still not compile, because A and B aren't polymorphic types. A needs to have a virtual function in order to make the types polymorphic. This will compile, but the cast will just throw std::bad_cast, since it isn't really a B*.
And even if it were a B*, it will fail in horrendous ways if you try to use it. Both std::auto_ptrs a and b will assume they own the object and free it later on, resulting in all kinds of memory corruption. You probably want to use a.release() after the cast was successful.
I think c++ stores RTTI (run time type information) in the vtable. Hence to use dynamic_cast<> with an instance object, the object needs have 'vtable'. C++ creates vtable only when at least one function is declared 'virtual' in the class.
The class A and Class B there are no virtual functions. This could be reason for the dynamic_cast failure. Try declaring a virtual destructor in base class.