I have a question about the following code:
let's say I have a class P that has a copy constructor and a regular constructor the receives one string value.
I have the following code:
P doSomething(){
P p("myValue");
return p;
}
int main(){
P m=doSomething();
return 1;
}
why isn't copy constructor invoked at the return p of the doSomething() function?
the call P m=doSomething() - does it suppose to call the copy constructor or the operator=?
in case it's operator =, what is the difference of this code and the following:
P new_val=("newVal");
p m=new_val;
(i know here the call is for copy constructor)
Thanks,
Mary
why isn't copy constructor invoked at the return p of the doSomething() function?
The standard allows this copy to be elided. Google for [N]RVO. On certain compilers this happens only when optimizing, on others it is part of the calling conventions and thus happens always.
the call P m=doSomething() - does it suppose to call the copy constructor or the operator=?
T t = x; is "syntactic sugar" (in the sense that the T(x) is happening implicitly) for T t(T(x)) and thus has -- despite the = -- nothing to do with operator=. Note that also here the additional temporary may be elided, thus no copy ctor is called.
in case it's operator =, what is the difference of this code and the following:
This code makes no sense, what did you really mean?
P new=("newVal");
p m=new;
I made a small sample for demonstration.
void print(char* text, int ident)
{
for(int i = 0 ; i < ident ; i++)
{
printf(" ");
} // end for
fprintf(stdout, text);
fprintf(stdout, "\n");
fflush(stdout);
}
class P
{
public:
char* _str;
P (P& arg)
{
print("copy constructor", 2);
arg._str = this->_str;
}
P (char* str)
{
print("constructor", 2);
_str = str;
}
};
P doSomething(){
print("do something - function", 1);
P p("myValue");
print("before return p", 1);
return p;
}
int main(int argc, char* argv[])
{
print("start", 0);
P m=doSomething();
print("stop - call return", 0);
return 1;
}
this returns
start
do something - function
constructor
before return p
copy constructor
stop - call return
so copy constructor WILL BE CALLED
1) why isn't copy constructor invoked at the "return p" of the doSomething() function?
it should - try printing something to the console in the copy c-tor to see if it is really running.
2) the call P m=doSomething() - does it suppose to call the copy constructor or the operator=?
should call the operator=. again, use the debug message print from within the method to check
3) in case it's operator =, what is the difference of this code and the following:
P new=("newVal"); p m=new; (i know here the call is for copy constructor)
did you miss something on the code snippet? i think it won't compile
Related
Let's say someString initialization is a tad complicated, hence we write a simple member function stringInitialization() with which to initialize someString in the body of the constructor:
class DemoL {
private:
int someNumber;
std::string someString;
void stringInitialization() {
if (someNumber == 1) {
someString = "FIRSTSTRING";
} else if (someNumber == 2) {
someString = "SECONDSTRING";
} else {
someString = "";
}
}
public:
explicit DemoL(int rNumber) :
someNumber(rNumber) {
stringInitialization();
}
};
This way, I'd assume, someString will be default initialized before the body of the constructor and only after that will it be modified by calling stringInitialization().
So, let's modify the code a bit, so that we initialize someString in the constructor initializer list:
class DemoL {
private:
int someNumber;
std::string someString;
std::string stringInitialization() const {
if (someNumber == 1) {
return "FIRSTSTRING";
} else if (someNumber == 2) {
return "SECONDSTRING";
} else {
return "";
}
}
public:
explicit DemoL(int rNumber) :
someNumber(rNumber),
someString(stringInitialization()) {}
};
Would you be so kind as to tell me whether the second variant is more efficient and correct?
Your assumption is correct. The constructor DemoL(int rNumber) needs to be able to default construct the member someString and this is no problem because std::string is default constructible.
One obvious point is that one member could be not default-constructible, in which case you would have to initialize it.
But even if it is, default constructing it could be a waste of resources, if you are changing it right after, so the second alternative seems better to me as it goes straight to the point.
However, since stringInitialization returns by value, I would use std::move, no, this is actually a bad idea, as pointed out in the comments.
So as a conclusion:
the two codes are the same as regards someNumber
the first code default-initializes someString ("calls" std::basic_string<...>::basic_string) and then assigns it (calls std::basic_string<...>::operator=)
the second code constructs the return std::string value from the sting literals when the call to stringInitialization returns (calls basic_string<...>::basic_string(const char*)), and then copy-constructs someString (calls basic_string<...>::basic_string(basic_string&&)); actually copy elision happens because stringInitialization returns an rvalue.
I am maintaining the rule of three like this --
// actual constructor
stuff::stuff(const string &s)
{
this->s_val = s[0];
this->e_val = s[s.length() - 1];
}
// copy constructor
stuff::stuff(const stuff &other)
{
this->s_val = other.s_val ;
this->e_val = other.e_val ;
}
// assignment
stuff& stuff::operator=(const stuff &other)
{
stuff temp(other);
*this = move(temp);
return *this;
}
now I can call like this --
stuff s1("abc");
stuff s2(s1);
stuff s3 = s2 ; // etc ...
Now I am trying to implement a function that will use operator= so that I can call like --
stuff s;
s = "bcd" ;
and I am writing like this --
stuff& stuff::operator=(const string &s)
{
stuff temp(s);
*this = move(temp);
return *this;
}
but it is giving me segfault. Moreover what should I do if want to call like
stuff s = "bcd" ?
how do I do that ?
you assignment operator and copy constructor should look the same :
stuff& stuff::operator=(const stuff &other)
{
this->s_val = other.s_val ;
this->e_val = other.e_val ;
return *this;
}
you can't define you = operator with another = operator .
remember, std::move doesn't do any special magic, it just turns a variable into somthing that can be cought using move semantics. you still need to define your function as one who deals with r-value-reference in the first place, which you didn't (you dont have any move assignment operator).
in you string-accepting = operator , you can use the regular one :
stuff& stuff::operator=(const string &s)
{
*this = stuff(s);
return *this;
}
few more suggestions :
your this-> is redundent. the compiler knows the variable you're refering is part of this.
also , the lines:
this->s_val = s[0];
this->e_val = s[s.length() - 1];
can be written more elegantly as:
s_val = s.first();
e_val = s.back();
btw. the copy ctor., assignemt operator and the destructor are redundent also.
the rule of three (or five, as C++11) says *IF* you implement any of the copy ctor. assigment operator or the destructor - you need to implement them all.
the question is, should you implement any of them at the first place? you don't have any dynamic allocation here, no shallow copying, and nothing special that needs special member functions (copy ctor. etc.).
you might as well delete the whole three and that will be the best case in your example.
Let's say in a class a constructor is overloaded. Can multiple data members be initialized for the single object using different constructors of the same class?
eg :
class demo{
int size;
double k;
public:
demo(int s){
size=s;
}
demo(double p){
k = size+p;
}
void show(){
cout<<size<<" "<<k<<"\n";
}
};
int main(){
demo a = demo(0);
a = 4.7;
a.show();
return 0;
}
Is this possible?
No, once the object is constructed, it's constructed.
Let's go through your code and see what it does (assuming no optimizations, please note that many modern compilers will do some copy-elision even in debug or -O0 modes):
demo(0);
The code demo(0) calls the demo(int s) constructor. A temporary rvalue is created. So now we have a temporary object with the values:
size = 0
k = uninitialized
demo a = demo(0);
demo a is then created using an implicit copy constructor.
We now have a demo object named a with the following values:
size = 0
k = uninitialized
a = 4.7;
Because a is already constructed, this will call an implicit assignment-operator. The default assignment-operator will copy all the values from one object into the other object. This means the 4.7 needs to be converted into a demo object first. This is possible because of your demo(double p) constructor.
So a temporary demo object will be created with the values:
size = uninitialized
k = uninitialized + 4.7 = undefined
These values will be copied into a and so both of a's data members will be undefined.
Possible Solutions
songyuanyao's solution of using a constructor with multiple parameters is one good way of doing it.
Using setters is another way.
Either way, I would recommend having your constructors provide default values for your data-members.
demo(int s)
{
size = s;
k = 0.0; // or some other suitable value
}
Here's how you could create a setter.
void setK (double p)
{
k = size + p;
}
You could then do this:
int main ()
{
demo a (0) ;
a.setK (4.7) ;
a.show () ;
return 0 ;
}
I think you're after the Builder Pattern.
You should define a ctor which can take multiple parameters to initialize multiple data members:
demo(int s, double p) {
size = s;
k = size + p;
}
and then
int main() {
demo a(0, 4.7);
a.show();
return 0;
}
My code is
class CTemp{
public:
CTemp(){
printf("\nIn cons");
}
~CTemp(){
printf("\nIn dest");
}
};
void Dowork(CTemp obj)
{
printf("\nDo work");
}
int main()
{
CTemp * obj = new CTemp();
Dowork(*obj);
delete obj;
return 0;
}
The output that I get is
In cons
Do work
In dest
In dest
Now why does the constructor get called once but the destructor is called twice? Can someone please explain this?
void Dowork(CTemp obj)
Here local-copy will be done, that will be destruct after exit from scope of DoWork function, that's why you see destructor-call.
Implement a copy constructor and check again:
CTemp(const CTemp& rhs){
printf("\nIn copy cons");
}
When the function is called its parameter is created by using the implicit copy constructor. Add to your class the following copy constructor
CTemp( const CTemp & ){
printf("\nIn ccons");
}
to see one more message about creating an object
I have a "abstract" super class called RealAlgebraicNumber and two inherited classes called IntervalRepresentation and NumericRepresentation. Both IntervalRepresentation and NumericRepresentation have a copy constructor and they work fine.
I use shared_ptr like this:
typedef std::tr1::shared_ptr<RealAlgebraicNumber> RealAlgebraicNumberPtr;
At another part of the programm I want to use the copy constructor for the abstract super class RealAlgeraicNumber:
RealAlgebraicPoint RealAlgebraicPoint::conjoin (const RealAlgebraicNumber& N)
{
vector<RealAlgebraicNumberPtr> v (mNumbers.begin(), mNumbers.end());
v.push_back(RealAlgebraicNumberPtr(new RealAlgebraicNumber(N)));
return RealAlgebraicPoint(v);
}
I did not define a copy constructor for RealAlgebraicNumber at all. I have no idea what it should do. The compiler is fine with the code, but unfortuantly when I test conjoin like this:
vector<RealAlgebraicNumberPtr> v;
v.push_back(RealAlgebraicNumberPtr(new NumericRepresentation(2)));
RealAlgebraicPoint PPP (v);
PPP.print();
PPP = PPP.conjoin (NumericRepresentation(3));
PPP.print();
The output is:
( 2 )( 2 null )
And print was defined like this:
void RealAlgebraicNumberFactory::print (const RealAlgebraicNumberPtr& A)
{
IntervalRepresentationPtr irA = std::tr1::dynamic_pointer_cast<IntervalRepresentation> (A);
NumericRepresentationPtr nrA = std::tr1::dynamic_pointer_cast<NumericRepresentation> (A);
if (irA != 0)
cout << irA->Interval();
else if (nrA != 0)
cout << static_cast<numeric>(*nrA);
else
cout << "null";
}
I use a loop to call the static-print function and put the representation between the ( ).
I tryed it the way Cat Plus Plus propused: virtual method in RealAlgebraicNumber,
virtual std::tr1::shared_ptr<RealAlgebraicNumber> clone();
implementation in e.g. NumericRepresentation
RealAlgebraicNumberPtr NumericRepresentation::clone()
{
return RealAlgebraicNumberPtr(new NumericRepresentation(*this));
}
And then used it like this in conjoin:
RealAlgebraicPoint RealAlgebraicPoint::conjoin (const RealAlgebraicNumber& N)
{
vector<RealAlgebraicNumberPtr> v (mNumbers.begin(), mNumbers.end());
v.push_back(RealAlgebraicNumberPtr(N.clone()));
return RealAlgebraicPoint(v);
}
Now the compiler complains:
RealAlgebraicPoint.cpp: In member function 'GiNaC::RealAlgebraicPoint GiNaC::RealAlgebraicPoint::conjoin(const GiNaC::RealAlgebraicNumber&)':
RealAlgebraicPoint.cpp:66:48: error: passing 'const GiNaC::RealAlgebraicNumber' as 'this' argument of 'virtual std::tr1::shared_ptr<GiNaC::RealAlgebraicNumber> GiNaC::RealAlgebraicNumber::clone()' discards qualifiers
I dont get it! Whats wrong?
Edit: Oke its fine! It had something to do with const, and virtual.
Thank you!
Joachim
If you didn't define a copy ctor, compiler will generate a default one, doing memberwise copy. What you probably want is polymorphic clone, to preserve the type, and call a proper copy ctor. For that, add a new virtual member, e.g. virtual RealAlgebraicNumber* clone();, and override it in every subclass to do return new T(*this); — then your conjoin will look like this:
RealAlgebraicPoint RealAlgebraicPoint::conjoin (const RealAlgebraicNumber& N)
{
vector<RealAlgebraicNumberPtr> v(mNumbers.begin(), mNumbers.end());
v.push_back(RealAlgebraicNumberPtr(N.clone()));
return RealAlgebraicPoint(v);
}