what is happened for constructors that accept objects as parameters ? - c++

i want ask what is actually meaning by objects conversion , for example:
why i can't access print at class B using foo after assignment it to A ?!
#include <iostream>
using namespace std;
class A {};
class B {
public:
// conversion from A (constructor):
B (const A& x) {}
void print(){cout << "Huo Jackman!\n";}
};
int main ()
{
A foo;
B bar = foo; // calls constructor
foo.print();
return 0;
}

Use:
bar.print();
Please note that the original foo object is not changed when bar is created. foo has type class A, which doesn't contain a print method, and in C++ it's not possible to add methods on the fly.

Your code is an example of Type Conversion. By doing the following
B (const A& x) {}
You are constructing a new object of Type B from an object of Type A. Here the original object A remains unchanged. When you write
A foo;
B bar = foo; // calls constructor
You have first created foo which is an object of Type A. From this object you are now creating an object of Type B taking foo as an argument. It will construct bar but will leave foo unchanged. That is why you cannot invoke the print method on foo since the print method is defined only for bar and not foo.
Now as you asked what would you gain by doing this.
Consider the following code
void fun ( B& b )
{
// do something with object b
}
void main ()
{
A a;
fun ( a );
}
The function fun here is expecting an object of type B but it is being passed an object of Type A. Before generating compile error, the compiler will try to see if there is any way it can construct object of Type B from an object of Type A. If you had not defined any conversion, the compiler will generate compile error. But here since we can construct an object of type B taking A as an argument, the compiler will do that and the call will succeed. This type of mechanism does have some unintended consequences. Here you wanted to call the function fun with object of type B but the call succeeded even with object of type A since compiler does an implicit conversion from Type A to Type B using the constructor in B. To stop this kind of behavior, keyword 'explicit' can be used on the affected constructor.
explicit B (const A& x) {}
Consider the following code inside class B
B& operator= (const A& x) {return *this;}
This will not create any new object. However it will be used modify an object of type B using an object of type A. If on the other hand we want to construct an object of Type A from an object of Type B we will have to use a typecast operator ( conversion function) in class B:
operator A() { return A(); }
So the class B would look something like:
class B
{
public:
B ( const A& a ) {} // conversion from A using constructor
B& operator= ( const A& a ) { return *this;} // conversion from A using assignment
operator A() { return A(); } // conversion to A using typecast operator
};
int main ( int argc, char** argv )
{
A a;
B b= a; // Construct B from A using constructor ( 1st one above )
b = a; // copy a into b using assignment operator ( 2nd one above)
a = b; // construct A from B using typecast operator ( 3rd one above )
return 0;
}
Please refer to this and this for some additional information

Related

Why does a function parameter call a constructor with an argument?

I have this code(stolen from cplusplus.com):
// explicit:
#include <iostream>
using namespace std;
class A {};
class B {
public:
explicit B (const A& x) {}
B& operator= (const A& x) {return *this;}
operator A() {return A();}
};
void fn (B x) {}
int main ()
{
A foo;
B bar (foo);
bar = foo;
foo = bar;
// fn (foo); // not allowed for explicit ctor.
fn (bar);
return 0;
}
So apparently the statement fn (foo); would call the constructor defined in class B, and I do not really get why. Why would that call a constructor with an argument, I mean aren't things simply copied to the function parameters when not using a reference? If fn (foo); calls the constructor B (const A& x) {}, shouldn't fn (bar); generate an error since there is no constructor defined for an argument of type B, such as B (const B& x) {}?
It's all because of fn()'s signature:
void fn (B x) {}
fn only accepts objects of type B, and passes them by value.
So what happens when calling fn() with an object of type A?
A foo;
fn (foo); // not allowed for explicit ctor.
The compiler tries to find the best matching function - a function called fn and receives type A. (Overload resolution)
Because there is no such function, it then tries to find the next best match by casting the object to a type that fn() accepts.
A conversion constructor would usually do the job : B (const A& x) {}
But because it's marked with an explicit specifier the compiler can't do it implicitly.
You would have to explicitly cast the object for the compiler to find a match:
fn ((B)foo); // Explicit conversion is allowed.
That is a result of an explicit creation of an object
fn(foo);
is not implicitly possible since fn() doesn't takes any A object as parameter
BUT your foo parameter can actually be used to create an instance of B which match perfectly the fn parameter, you can prevent that implicitly conversion of objects using the explicit keyword.
And that is what you are doing here:
explicit B (const A& x) {}
From the C++ Standard
15 The initialization that occurs in the = form of a brace-or-equal-initializer or condition (6.4), as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.6.1), is called copy-initialization. [ Note: Copy-nitialization may invoke a move (12.8).—end note ]
Thus for the explicit constructor
explicit B (const A& x) {}
this initialization that in fact is used for the initialization of the function parameter
A foo;
B bar = foo;
is wrong.
It is the direct initialization that can be used with the explicit constructor as it is shown in your program
A foo;
B bar( foo );
Well ... B(const B&) although not explicitly declared, is there. That's because it is one of the special functions for which the compiler has a default:
B()
B(const B&)
B(B&&)
B& operator=(const B&)
B& operator==(B&&)
~B()
So, B can copy itself.
It cannot convert implicitly form A being such a conversion is declared as explicit.
fn(B(foo)) would have worked.

Polymorphism in C++: Calling an overridden method

First, I'm Java coder and want to understand polymorphism in c++. I wrote the example for learning purposes:
#include<iostream>
using namespace std;
class A
{
public:
virtual void foo(){ std::cout << "foo" << std::endl; }
};
class B : public A
{
public:
void foo(){ std::cout << "overriden foo" << std::endl; }
};
A c = B();
int main(){ c.foo(); } //prints foo, not overriden foo
I expected that overriden foo would be printed, but it wasn't. Why? We overrode the method foo in the class B and I thought that the decision which method should be called is being making from the runtime type of the object which in my case is B, but not a static type (A in my case).
Live example is there
When you do this:
A c = B();
You're converting the B value into A. You don't want that.
You should make a B object and access it through an A pointer or reference to get polymorphic behaviour:
B b;
A& c = b;
In java, you have value semantics with types like int and float, and you have reference semantics with everything else.
That's not the case in C++: the type system is unified, and you get whichever of value or reference semantics that you ask for.
With the code you've written
A c = B()
you've told the compiler to create a new value of type B, and then convert it to a value of type A, storing the value in c. Conversion, in this case, means taking A data out of the new B instance you created it, and copying it into the new A instance stored in c.
You could do this instead:
B b;
A &c = b;
This still creates the value b, but now c is a reference to A, which means c will now refer to the B instance you created, rather than being a copy of its A part.
Now, this still creates b as a local variable, and the object stored in b gets destroyed as soon as b goes out of scope. If you wanted something more persistent, you'd need to use pointers; e.g. something like
shared_ptr<A> c = make_shared<B>();
c->foo();
You could do something more 'raw' like
A *b = new B();
but this is a 'dumb' pointer; shared_ptr is smarter and your object will get destroyed when nothing else references it. If you do the latter, you'd have to do the destruction yourself when appropriate (and messing this up is a common source of mistakes)
Your confusion stems from a crucial difference between Java and C++.
In Java if you write
MyClass var = whatever;
your variable var is a reference to the object returned by whatever. However, in C++ this syntax means "create a new object of type MyClass by passing the result of the expression whatever to an appropriate constructor, and copy the resulting object into the variable var.
In particular, your code creates a new object of type A, named c, and passes a temporary default-constructed object of type B to its copy constructor (because that's the only constructor that fits). Since the newly created object is of type A, not of type B, obviously A's method foo is called.
If you want to have a reference to an object, you have to explicitly request that in C++, by adding & to the type. However a reference to non-constant objects cannot be bound to temporaries. therefore you need to explicitly declare also the object you bind to (or alternatively, use a reference to a const object, and fix your foo member functions to be const, since they don't change the object anyway). So the simplest version of your code doing what you want would read:
// your original definitions of A and B assumed here
B b; // The object of type B
A& c = b; // c is now a *reference* to b
int main() { c.foo(); } // calls B::foo() thanks to polymorphism
However the better version would be const-correct, and then could use your original construction:
#include <iostream>
class A
{
public:
virtual void foo() const // note the additional const here!
{ std::cout << "foo" << std::endl; }
};
class B : public A
{
public:
void foo() const // and also const here
{ std::cout << "overridden foo" << std::endl; }
};
A const& c = B(); // Since we bind to a const reference,
// the lifetime of the temporary is extended to the
// lifetime of the reference
int main() { c.foo(); } //prints overridden foo
(note that I removed using namespace std; because it's a bad thing to do (and your code used explicit std:: anyway, so it's just redundant).
Note however, that C++ references are still different from Java references in that they cannot be reassigned; any assignment goes to the underlying object instead. For example:
#include <iostream>
class A { public: virtual void foo() const { std::cout << "I'm an A\n"; } };
class B: public A { public: void foo() const { std::cout << "I'm a B\n"; } };
class C: public A { public: void foo() const { std::cout << "I'm a C\n"; } };
B b;
C c;
int main()
{
A& ref = b; // bind reference ref to object b
ref.foo(); // outputs "I'm a B"
ref = c; // does *not* re-bind the reference to c, but calls A::operator= (which in this case is a no-op)
ref.foo(); // again outputs "I'm a B"
}
If you want to change the object you refer to, you'll have to use pointers:
// definitions of A, B and C as above
int main()
{
A* prt = &b; // pointer ptr points to b
prt->foo(); // outputs "I'm a B"
prt = &c; // reassign ptr to point to c
prt->foo(); // outputs "I'm a C"
}
The line of interest is this (using uniform initialization syntax instead):
A c = B{};
It is important to note that, when declared this way, c behaves like a value.
Your code constructs a local A named c out of an instance of B. This is called slicing: Any part of that B that isn't also part of an A has been "sliced" away, leaving only an A.
Giving a symbol reference semantics in C++ (called indirection) requires a different notation.
For example:
A &c = B{};
A d = B{}; // Casts the intermediate B instance to const A &,
// then copy-constructs an A
c.foo(); // calls B::foo because c points to a B through an A interface
d.foo(); // calls A::foo because d is only an instance of an A
Note that the lifetime of the intermediate B to which c points is automatically extended to the scope of c. On the other hand, the second intermediate B is destroyed after the construction of d has completed.
In C++, references are immutable (they cannot be changed after initialization). When used in an expression, it is as though the object (value) to which they are pointing were used instead:
A &c = B{};
c = A{}; // Calls the compiler-provided A::operator = (const A &)
// (a virtual assignment operator with this signature
// was not defined).
// This DOES NOT change where c points.
Pointers, on the other hand, can be changed:
A a{};
B b{};
A *cptr = &b;
cptr->foo(); // calls B::foo
cptr = &a;
cptr->foo(); // calls A::foo
exactly what orlp said. you should also learn to use pointers too (they are fun)
A* c = new B();
c->foo(); // overriden foo
delete c;

Why it works when I assign the object with others' object?

I'd like to assign the object of type B to object of type A, but i don't know why it works with different types for the assignment?
#include <stdio.h>
class B
{
public:
B()
{
printf("B default constructor.\n");
}
};
class A
{
public:
A()
{
printf("A Default constructor.\n");
}
A(B const& b) // if add the tag "explicit" for the constructor, it will not work...
{
printf("User constructor.\n");
}
A(const A& a)
{
printf("copy-constructor.\n");
}
void get(){printf("A::get\n");}
};
int main()
{
A a = B(); // What's the meaning to assign object of type B to object of type A?
Why it works with above line?
How it works when do this?
a.get();
}
Every constructor that can be called with a single argument defines an implicit conversion to a class type. So the constructor:
A(B const& b)
is a conversion constructor. If this type of conversion is not useful, you've found the answer: declaring it as explicit can prevent it:
explicit A(B const& b)
I think your problem comes from thinking it is assignment. But A a = B(); is initialization. a is not first created with default constructor and then assigned to, it is directly constructed. And while I don't have the standard reference handy, if you don't have the constructor but have the assignment operator overload, that line will not compile, because it is not assignemnt, it needs the right constructor. To have assignment, try
A a;
a = B(); // actually assigns A(B()) implicitly if no operator= overload
If all data members of a class can be assigned, then objects of the class can be assigned, so that's why a = A(); works without adding any code.
If you want to block the implicit conversion, make the constructor explicit (example in the other answer).

How do I use a conversion constructor with pointers?

I have a class C that can be converted to class A and a function that takes an A* as an argument. I want to call it with a C*, but I can't seem to get a conversion constructor to work. I get: error: cannot convert ‘C*’ to ‘A*’ for argument ‘1’ to ‘void doSomething(A*)’. What am I doing wrong?
class C {
};
class A {
public:
A(C* obj) {}
};
void doSomething(A* object);
int main()
{
C* object = new C();
doSomething(object);
}
Conversion constructors can only be defined for user defined types, in your case A. However, they do not apply to fundamental types as pointers like A*.
If doSomething was taking an A const& instead (or simply an A), then the conversion constructor would be invoked as you expect.
If you main requirement is to be able to call the existing doSomething function, then you can do this:
int main()
{
C* object = new C();
A a(object);
doSomething(&a);
// May need to delete object here -- depends on ownership semantics.
}
You probably mean that you want C to be a subclass of A:
class C : public A {
...
};

How do I construct a base class which receives a reference argument?

I can't compile the following code using g++ 4.1.2:
#include <memory>
class A
{
public:
};
std::auto_ptr<A> GetA()
{
return std::auto_ptr<A>(new A);
}
class B
{
B(std::auto_ptr<A>& pA)
{
}
};
class C : public B
{
C() : B(GetA())
{
}
};
I get:
invalid cast of an rvalue expression of type std::auto_ptr<A> to type std::auto_ptr<A>&
The problem is I can't define a variable and pass its reference because I am inside a initialization list.
How can I do that when I am only allowed to change class C?
If you can only change C, you could do something like:
class C: public B
{
explicit C(std::auto_ptr<A>& pA) : B(pA) {}
public:
static C *GetC()
{
std::auto_ptr<A> pA = GetA();
return new C(pA);
}
};
The problem is trying to bind a non-const reference to the temporary returned by GetA. If you can assign that to a variable first, you have an lvalue and it works ok.
As Alex B says said (deleted answer), if you can change B, it would be better to take that auto_ptr argument by value; and if you can change compiler, it would be better to use unique_ptr and move semantics instead.
Depends on what B does with the passed in reference.
If B is going to take a copy of the argument, then make the argument a const &
If B is going to hold a reference to the argument then you need whatever is passed in to have a longer lifetime than the instantiation of B. (As mentioned already, the temporary created by a call to GetA() doesn't.)