Question on this overload program

  • Thread starter yungman
  • Start date
  • Tags
    Program
In summary: This variable goes out of scope after the constructor finishes, so the address becomes invalid. That's why when you try to print out the name in operator+(), you get garbage.To fix this, you should allocate memory for the "name" variable using the "new" keyword, similar to what you are doing in the other constructors. This will ensure that the variable's address remains valid even after the constructor finishes.In summary, the issue with the garbage output is due to assigning the address of a local variable to a member variable. To fix this, you should allocate memory for the variable using the "new" keyword. This will ensure
  • #1
yungman
5,718
241
Hi

I modified the program originally from Jtbell. I don't want to hijack his thread, so I ask my questions in this thread. I change names, mainly I assign names instead of a, b, c. I have question on one part of the program:

C++:
#include <iostream>
#include "OverLoad.h"
#include <cstring>
using namespace std;

int main()
{   char Ara[] = "First", Arb[] = "Second", Arc[] = "Result";
    Vec First(Ara, 1, 2);
    cout << " In main,  &First = " << &First << "\n\n";
    Vec Second(Arb, 3, 4);
    cout << " In main,  &Second = " << &Second << "\n\n";
    Vec Result(Arc);
    cout << " In main,  &Result = " << &Result << "\n\n";
    Result = First + Second;
    cout << " &Result = " << &Result << " Result = " << Result << "\n\n";
    return 0;
}

This is header file:
C++:
#ifndef OverLoad_H
#define OverLoad_H
#include <iostream>
#include <cstring>
using namespace std;
class Vec
{private:   double x, y;
public:
     char* name;
     Vec(char* desc)
        { x = 0;  y = 0;   name= new char[strlen(desc)+1];
          strncpy_s(name, strlen(desc) + 1, desc, strlen(desc) + 1);
          cout <<" [In Constructor(name)],     Object created is:   "<<name <<
           "("<<(*this).x <<", "<<(*this).y << "),   address is "<< this << "\n";
        }
     Vec(char* desc, double x0, double y0)
        { x = x0;  y = y0; name = new char[strlen(desc) + 1];
          strncpy_s(name, strlen(desc) + 1, desc, strlen(desc) + 1);
          cout << " [Constructor(name,x0,y0)]   Object created is:   "<< name <<
           "("<<(*this).x << ", "<<(*this).y << "),   address is "<<this <<"\n";
        }// this print out name of the object created.
     Vec(const Vec& original)
        { char tp[] = "Temp"; x = original.x;  y = original.y;  name = tp;
          cout << "[Copy Constructor],  original: " << original.name <<
           "\n    Object created: "<<(*this).name <<",     address: "<< this<<endl;
        }// this print out name of the object created.
     ~Vec()
        {cout<<"\n object destroyed: "<<(*this).name <<",     address: "<<this<<endl;}
     const Vec operator+ (const Vec& rhs) const
        { cout << " [In op+] \n"; char Ars[] = "sum";
          Vec sum(Ars);
          cout << " \n declare " << sum.name << ", address: " << &sum <<
             ",   rhs is: " << rhs.name << ",  address: " << &rhs << endl;
          sum.x = x + rhs.x;  sum.y = y + rhs.y;
          cout <<"\n "<< sum.name << " "<< sum <<" = "<<*this<<" + "<<rhs<<"\n\n\n";
          cout << " [To operator+],   ";
          return sum;
        }
     Vec& operator= (const Vec& rhs)//cout << rhs.name don't work, but &rhs works why?
        { cout << "\n [in op=] rhs.name:" << rhs.name << ",  address: " << &rhs;
          x = rhs.x;   y = rhs.y;
          cout << "\n Address of copied object:     this =" << this << *this << endl;
          return *this;
        }
     friend ostream& operator<< (ostream& out, const Vec& v)
        { out << "[In op<<] " << "(" << v.x << "," << v.y << ")";
          return out; }
};
#endif

1) first question: If you look at the word file, there are garbage when I try to print out the name. Please look at the header file, This is from running line 37 in operator+() jumping to operator+(). I try to print out rhs.name, that gives me the garbage. By if you look at line 23 in Copy Constructor, I assigned "Temp" to the name of the object created. I even verified by printing out the name in line 25 and it works.

When going to operator+(), object Temp is passed to &rhs by reference. I should be able to get "Temp" when I cout << rhs.name; But I got garbage. I double check &rhs = &Temp. Why the name doesn't follow?2) When I want to pass the c-string name from main to either Constructor, I have to create dynamic memory( name = new char[strlen(desc) + 1]), then using strncpy_s() to copy. I tried declaring a char array[] to copy in, it won't work. Is this the only way when passing c-string?

Thanks
 

Attachments

  • Temp overload.docx
    11.4 KB · Views: 212
Technology news on Phys.org
  • #2
The problem is in Vec(const Vec& original). You do a shallow copy of a c-string (temp). Not only does that not copy the string (only the pointer), temp is also stack allocated and goes out of scope, and is destroyed, at the end of the function.

You need a deep copy. So you have to delete the old string, allocate memory for the new one, and then copy over the characters from the one to the other.
 
  • Like
Likes yungman
  • #3
Jarvis323 said:
You need a deep copy. So you have to delete the old string, allocate memory for the new one, and then copy over the characters from the one to the other.

Since yungman are clearly heading into C++ territory and only use the string to give vectors a name while debugging, I will recommend he just switch to using std::strings instead.
 
  • Like
Likes Vanadium 50
  • #4
No, the whole exercise is pointless. The compiler will optimise away any code that doesn't do anything (such as, in this case, creating a temporary object on the stack to hold a result and then immediately copying that object somewhere else). The moment you put in code with a side effect (such as incrementing a static counter, writing to a stream, creating a string etc.), you prevent the compiler from optimising it away.

It's like trying to measure which slit the photon goes through in an interference pattern - the moment you do that, the pattern is no longer there.
 
  • Like
Likes harborsparrow, Vanadium 50 and Filip Larsen
  • #5
Of course it would not be a good idea to have a string be part of a vec class. But if for some reason you had that requirement then it might be a good idea to at least have it occupy a fixed size chunk of memory contiguous with the other fields. That way you have each vec is in one contiguous part of memory, an array of vec is one contiguous chunk of memory, and you have the convenience that you can directly index into the chunk of memory for IO and stuff like that.

A potential use case would be if you need to couple categorical data with numerical data. Even in that case, I think you would be better off composing a vec with a string in a new struct, so that a vec is just a vec.

C:
// will be contiguous in memory, with sizeof(Vec_Str<T,N>) = sizeof(T) + N*sizeof(char)
template< class T, int N >
struct Vec_Str
{
    Vec3< T > vec;
    char str[ N ];
    Vec_Str( const Vec_Str< T, N > & other )
    {
        for( int i = 0; i < N; ++i )  {
            str[ i ] = other.str[ i ];
        }
        vec = other.vec;
    }
};

And if you need to have dynamic strings, then you might as well use std::string, or a custom string class, which does a deep copy in it's operator= overload already. But beware that std::string is heap allocated, and the values in your struct will not be contiguous in memory and you will need to account for that with IO, for example.

C:
// data not contiguous in memory, with sizeof(Vec_Str<T>) = sizeof(T) + sizeof(std::string)
template< class T >
struct Vec_Str
{
    Vec3< T > vec;
    std::string str;
    Vec_Str( const Vec_Str< T > & other )
    {
        str = other.str
        vec = other.vec;
    }
};
 
Last edited:
  • #6
Jarvis323 said:
The problem is in Vec(const Vec& original). You do a shallow copy of a c-string (temp). Not only does that not copy the string (only the pointer), temp is also stack allocated and goes out of scope, and is destroyed, at the end of the function.

You need a deep copy. So you have to delete the old string, allocate memory for the new one, and then copy over the characters from the one to the other.
Thanks for the reply, I agree that the Temp will be destroyed as soon as it goes out of scope, but you can see the code and the printout that it is not out of scope as the very next step is copying from Temp into object Result. It has to be still valid when I ask to display the name.

Thanks
 
  • #7
pbuk said:
No, the whole exercise is pointless. The compiler will optimise away any code that doesn't do anything (such as, in this case, creating a temporary object on the stack to hold a result and then immediately copying that object somewhere else). The moment you put in code with a side effect (such as incrementing a static counter, writing to a stream, creating a string etc.), you prevent the compiler from optimising it away.

It's like trying to measure which slit the photon goes through in an interference pattern - the moment you do that, the pattern is no longer there.
It does give me a better feel of what's going on. I already learn a lot in Jtbell's thread, Here, I want to have a way to make and display the name that follows from all the manipulations. Just an excuse to get more familiar with C++.

The program works, that's not even an issue, just want to get a better feel of how the compiler works.

Thanks
 
  • #8
Filip Larsen said:
Since yungman are clearly heading into C++ territory and only use the string to give vectors a name while debugging, I will recommend he just switch to using std::strings instead.
Thanks

Can you clarify what you mean? I thought I have been studying C++ all along! You mean all the former 12 chapters I studied are just a warm-up? 😊 :smile:

I actually started out using std::string all along and just switch back to c-string lately! Main reason is I was working on a vector of structure and I need every single element to be the same size. If I use std::string, every name is of different size and thereby every element of the vector of structure will be different size and very hard to do searching and all. So I went back to fixed sized c-string that I can set the size to say 25 char for the name. Yes, in this case is not important, but the program I worked on matters.

I know my thread here doesn't make much sense, Jtbell already exhausted this topic already. I just find it very interesting, like a back door way to understand the compiler a little better, AND give me a reason to play around with the operator overload in as different light. Of cause I would not use this as method of debug unless it's called for, just for the fun of it.

But I think the first question is valid. Because object Temp is still in use and you can see Temp is destroyed in later lines, not when I try to display the name. It doesn't make sense I get garbage...even though the final program works.Thanks
 
  • #9
yungman said:
Can you clarify what you mean? I thought I have been studying C++ all along! You mean all the former 12 chapters I studied are just a warm-up?
I don't think that's what he (Filip Larsen) meant, only that it didn't make sense to be using a C-string in the context of a class.
yungman said:
But I think the first question is valid. Because object Temp is still in use and you can see Temp is destroyed in later lines, not when I try to display the name. It doesn't make sense I get garbage...even though the final program works.
I didn't try out your code, but jarvis gave the answer back in post #2. Your copy constructor isn't doing a deep copy. This is exactly the same thing that I was talking about in another thread of yours where I showed a small piece of code in which I didn't supply a copy constructor, but instead relied on the one provided by the compiler.
In a shallow copy, the data and pointer values are copied over. If two objects are pointing to the same data, and one object gets destroyed due to going out of scope, The other object's pointer will likely be pointing to garbage.
 
  • Like
Likes yungman
  • #10
Mark44 said:
I don't think that's what he (Filip Larsen) meant, only that it didn't make sense to be using a C-string in the context of a class.

Exactly.

For yungmans benefit I feel the urge to add that using raw C-strings in a C++ is really a no-go today unless one has good reasons to do so. Avoiding C-strings and manual allocations/deallocation is the first important steps away from shooting yourself in the foot all the time using C++. It may not be apparent with small toy programs, but for (newer) industry strength systems its a must.

yungman said:
I actually started out using std::string all along and just switch back to c-string lately! Main reason is I was working on a vector of structure and I need every single element to be the same size. If I use std::string, every name is of different size and thereby every element of the vector of structure will be different size and very hard to do searching and all.

Not so. A std::string, like any other variable size container in the standard library, actually takes up a constant amount of storage in the class where you use them, that is, sizeof(std::string) is constant. On one of the online gcc compilers for instance its 8 bytes, on another system or compiler it could be different but still constant. Of course, the string data that is managed by the string instance also takes up space somewhere, but that is on the heap (allocated with new[] or similar), potentially far away from where the string instance itself resides.
 
  • Like
Likes harborsparrow and pbuk
  • #11
Filip Larsen said:
Of course, the string data that is managed by the string instance also takes up space somewhere, but that is on the heap (allocated with new[] or similar), potentially far away from where the string instance itself resides.
Which makes it all the more important to have a copy constructor (and others) that does the copy in a smart way.
 
  • Like
Likes pbuk
  • #12
Mark44 said:
Which makes it all the more important to have a copy constructor (and others) that does the copy in a smart way.

Indeed, or when using the std::string and similar classes with value semantics as members in your class, just go without copy constructors and let the default copy constructor handle it. I find I rarely have to write a copy constructor in the production C++ I'm involved with because the classes are usually already tuned to behave with the desire value or reference semantics. If I need deep copy on a member with reference semantics I would probably add a DeepCopy method instead of having it hidden in a copy constructor that can be invoked in all sorts of surprising situations. Or in other words, if there is a need for deep copying a member in a copy constructor then that is a smell that maybe changing the member to a class with value semantics is cleaner.

Of course, the classes at the "bottom layer" (like the std::string) that operate on allocated memory chunks will of course need a copy constructor as part of providing value semantics. But normal "business logic" classes that does not allocate anything themselves should hopefully not care about this.
 
  • #13
Mark44 said:
I don't think that's what he (Filip Larsen) meant, only that it didn't make sense to be using a C-string in the context of a class.
I didn't try out your code, but jarvis gave the answer back in post #2. Your copy constructor isn't doing a deep copy. This is exactly the same thing that I was talking about in another thread of yours where I showed a small piece of code in which I didn't supply a copy constructor, but instead relied on the one provided by the compiler.
In a shallow copy, the data and pointer values are copied over. If two objects are pointing to the same data, and one object gets destroyed due to going out of scope, The other object's pointer will likely be pointing to garbage.
I did not know that. I took it very literally. You mean it gets destroy even before the Destructor as it was very clear Destructor is called to destroy Temp in LATER lines.

You mean there's a deep copy and "shallow" copy? Where do you get all these information...Maybe I really don't want to know at this point as I am drowning with new information already! :)).

thanks
 
  • #14
Filip Larsen said:
Exactly.

For yungmans benefit I feel the urge to add that using raw C-strings in a C++ is really a no-go today unless one has good reasons to do so. Avoiding C-strings and manual allocations/deallocation is the first important steps away from shooting yourself in the foot all the time using C++. It may not be apparent with small toy programs, but for (newer) industry strength systems its a must.
Not so. A std::string, like any other variable size container in the standard library, actually takes up a constant amount of storage in the class where you use them, that is, sizeof(std::string) is constant. On one of the online gcc compilers for instance its 8 bytes, on another system or compiler it could be different but still constant. Of course, the string data that is managed by the string instance also takes up space somewhere, but that is on the heap (allocated with new[] or similar), potentially far away from where the string instance itself resides.

How come all the examples I saw in the book using c-string and pointer new char[size] to copy name over? These are all new to me, I don't really get what you are saying.

You mean if I use std::string for this purpose, they will be constant size? What if I have last name like Wong which is very short and another last name of some European name that is 20 characters? The compiler will know and use the longest name as the size in a vector of structures or save in files? Far as I understand like files, they don't really care about your elements of vector or anything, it just go to the location you give and take out certain amount of bytes.

Thanks
 
  • #15
Hi guys

I thought I do this experiment to go a step deeper, but it sounds like it's going a lot deeper than I thought. All the deep copy and how the compiler might think... If so, maybe I should stop digging for now. I have plenty to learn right now, if this is not the time to open this can of worm, I am more than happy to drop this thread and go back to my studying. I am sure the book say nothing of these yet. I still have not finish chapter 14 that I should go back to.

Thanks
 
  • #16
I am trying to wrap up this program, I notice I forgot to delete the dynamic allocated memory in the Destructor as I added the name = new char[size] everywhere. So I added the line delete name[]. The debugger gave me this:

Error.jpg


What is this?
Edit:

I stepped through, it was going into Destructor of Temp object when things went wrong. it gave me this page:
Error1.jpg


thanks
 
Last edited:
  • #17
Guys

I am not sure I can agree reading rhs.name gives me garbage is about not a deep copy. The reason is we all know:

1) in operator+(), temporary object sum is created to store Left + Right.
2) Then sum is copied into object Temp that is another temporary object.
3) Sum is destroyed.
4) Temp is copied into Result by operator=() where parameter &rhs is reference to Temp.

I know that because in operator=(), x = rhs.x, y = rhs.y. This copy the value into Result to display in line 15 of main(see post 1).

BUT remember, the values of x and y were copied over to Result by operator=() in line 39 of header file. Then using operator <<() to display. It will display Result.name = "Result".BUT if you look at both operator=() and operator<<(). ONLY x and y is being copied over and nobody touch the name in Result. That's the reason when display Result.name, you get "Result".

So I did an experiment. I modify the operator=() and operator<<() by adding name=rhs.name and output v.name resp. You can see the printout that when I display Result, I got garbage.
C++:
     Vec& operator= (const Vec& rhs)//cout << rhs.name don't work, but &rhs works why?
        { //cout << "\n [in op=] rhs.name:" << rhs.name << ",  address: " << &rhs;
          x = rhs.x;   y = rhs.y;
          name = rhs.name;//adding this line
          cout << "\n Address of copied object:     this =" << this << *this << endl;
          return *this;
        }
     friend ostream& operator<< (ostream& out, const Vec& v)
        { out << "[In op<<] " << "(" << v.name << v.x << "," << v.y << ")";//adding v.name.
          return out; }
ONLY one way can be right. I showed x and y are copied over from Temp to Result. BUT in the same function after I added the name, the garbage is copied over. I don't think this is about deep copy. The information in name is lost.

This is the copy of the printout of the few lines executed:

printout.jpg


You can see Temp is destroyed in the second to the bottom line with the correct address. Last line is from line 15 of main verified with correct address. you can see the garbage, BUT you can see correct value of x and y as show. How do you explain by deep copy?

Something is really wrong here. Program is NOT working. If you have a c-string that you need to pass on, it will give you garbage. It just happened the two number got passed correctly.

ThanksJust in case, this is the modified header file:
C++:
#ifndef OverLoad_H
#define OverLoad_H
#include <iostream>
#include <cstring>
using namespace std;
class Vec
{private:   double x, y;
public:
     char* name; 
     Vec(char* desc)
        { x = 0;  y = 0;   name= new char[strlen(desc)+1];
          strncpy_s(name, strlen(desc) + 1, desc, strlen(desc) + 1);
          cout <<" [In Constructor(name)],     Object created is:   "<<name <<
           "("<<(*this).x <<", "<<(*this).y << "),   address is "<< this << "\n";
        }
     Vec(char* desc, double x0, double y0)
        {
          x = x0;  y = y0; name = new char[strlen(desc) + 1];
          strncpy_s(name, strlen(desc) + 1, desc, strlen(desc) + 1);
          cout << " [Constructor(name,x0,y0)]   Object created is:   "<< name <<
           "("<<(*this).x << ", "<<(*this).y << "),   address is "<<this <<"\n";
        }// this print out name of the object created.
     Vec(const Vec& original)
        { char tp[] = "Temp"; x = original.x;  y = original.y;  name = tp;
          cout << "[Copy Constructor],  original: " << original.name <<
           "\n    Object created: "<<(*this).name <<",     address: "<< this<<endl;
        }// this print out name of the object created.
     ~Vec()
        {cout<<"\n object destroyed: "<<(*this).name <<",     address: "<<this<<endl; /*delete[]name;*/}

    
         const Vec operator+ (const Vec& rhs) const
        { cout << " [In op+] \n"; char Ars[] = "sum";
          Vec sum(Ars);
          cout << " \n declare " << sum.name << ", address: " << &sum <<
             ",   rhs is: " << rhs.name << ",  address: " << &rhs << endl;
          sum.x = x + rhs.x;  sum.y = y + rhs.y;
          cout <<"\n "<< sum.name << " "<< sum <<" = "<<*this<<" + "<<rhs<<"\n\n\n";
          cout << " [To operator+],   ";
          return sum;
        }
     Vec& operator= (const Vec& rhs)//cout << rhs.name don't work, but &rhs works why?
        { //cout << "\n [in op=] rhs.name:" << rhs.name << ",  address: " << &rhs;
          x = rhs.x;   y = rhs.y;
          name = rhs.name;//Added for testing
          cout << "\n Address of copied object:     this =" << this << *this << endl;
          return *this;
        }
     friend ostream& operator<< (ostream& out, const Vec& v)
        { out << "[In op<<] " << "(" << v.name << v.x << "," << v.y << ")";//Added for testing.
          return out; }
};
#endif
 
Last edited:
  • #18
yungman said:
C++:
const Vec operator+ (const Vec& rhs) const
        { cout << " [In op+] \n"; char Ars[] = "sum";
          Vec sum(Ars);                                       // <---- 1) This line is a problem
          cout << " \n declare " << sum.name << ", address: " << &sum <<
             ",   rhs is: " << rhs.name << ",  address: " << &rhs << endl;
          sum.x = x + rhs.x;  sum.y = y + rhs.y;
          cout <<"\n "<< sum.name << " "<< sum <<" = "<<*this<<" + "<<rhs<<"\n\n\n";
          cout << " [To operator+],   ";
          return sum;
        }

C++:
 Vec& operator= (const Vec& rhs)//cout << rhs.name don't work, but &rhs works why?
        { //cout << "\n [in op=] rhs.name:" << rhs.name << ",  address: " << &rhs;
          x = rhs.x;   y = rhs.y;
          name = rhs.name;//Added for testing                // <---- 2)This is also a major problem
          cout << "\n Address of copied object:     this =" << this << *this << endl;
          return *this;
        }
Regarding comment 1: Why is operator+() calling a copy constructor? The code from jtbell didn't call a copy constructor from within its operator+() implementation. Why did you make this change?
Also, we've been through this before -- please choose better names for variables. Ars is not a useful name -- is it an abbreviation for Arse? If not, Ars is in fact a terrible name. Maybe you can keep it straight, but you repeatedly ask us for help. Poor choices for variable names make it harder for us to get at what your code is trying to do.

Regarding comment 2: What do you think happens with name=rhs.name?
If the name field of the rhs object is allocated on the heap, what do you think happens to rhs.name when rhs goes out of scope (and is destroyed)?
 
  • Like
Likes Vanadium 50
  • #19
Mark44 said:
Regarding comment 1: Why is operator+() calling a copy constructor? The code from jtbell didn't call a copy constructor from within its operator+() implementation. Why did you make this change?
Also, we've been through this before -- please choose better names for variables. Ars is not a useful name -- is it an abbreviation for Arse? If not, Ars is in fact a terrible name. Maybe you can keep it straight, but you repeatedly ask us for help. Poor choices for variable names make it harder for us to get at what your code is trying to do.
I don't know why operator+() call copy constructor exactly, I thought it was like this in Jtbell's program with ThreeVector c; c = a + b; I take it operator+() created sum = b.operator(a) Then when "return sum", that triggered Copy Constructor to create object Temp and copy object sum over as sum is going to be destroy the moment exiting out of operator+(). Then operator= () is called to copy Temp into c. These steps are shown both in my program and Jtbell's program.

I did not change the flow of the program from Jtbell, I just added the name to make it easier to track by displaying the name all along.


Mark44 said:
Regarding comment 2: What do you think happens with name=rhs.name?
If the name field of the rhs object is allocated on the heap, what do you think happens to rhs.name when rhs goes out of scope (and is destroyed)?
It should be exactly the same as x = rhs.x; y = rhs.y in line 44 of the header file. I declared char* name in the public of the class definition. So the class has x, y and name as member variables.

Put it in another way. Jtbell has 3 variable x, y and z. I have 3 variables x, y and name. Same header file should pass and display them correctly whether it's z or name. Nothing got changed on this between Jtbell's program and mine.
Thanks
 
  • #20
yungman said:
I don't know why operator+() call copy constructor exactly,
You put it in there. If I recall correctly, jtbell's code for operator+() started off like this:
C++:
ThreeVector ThreeVector::operator+ (const ThreeVector& rhs) const
{
    ThreeVector sum;
    sum.x = x + rhs.x;  sum.y = y + rhs.y;  sum.z = z + rhs.z;
No copy constructor.
yungman said:
I thought it was like this in Jtbell's program
No, see above.
yungman said:
I did not change the flow of the program from Jtbell, I just added the name to make it easier to track by displaying the name all along.
I just pointed out how you changed the behavior of operator+(), which of course changes the behavior of your program.
yungman said:
It should be exactly the same as x = rhs.x; y = rhs.y in line 44 of the header file. I declared char* name in the public of the class definition. So the class has x, y and name as member variables.
No, it's not the same. The data members x, y, and z are qualitatively different from the member you added - name.

What are the types of x, y, and z?
What is the type of name?

Some questions I asked in my previous post:
What do you think happens with name=rhs.name;? Hint: It's not the same as, say, x = rhs.x; .
Regarding the assignment statement above, if the name field of the rhs object is allocated on the heap, what do you think happens to rhs.name when rhs goes out of scope (and is destroyed)?
 
  • Like
Likes Vanadium 50
  • #21
Mark44 said:
You put it in there. If I recall correctly, jtbell's code for operator+() started off like this:
C++:
ThreeVector ThreeVector::operator+ (const ThreeVector& rhs) const
{
    ThreeVector sum;
    sum.x = x + rhs.x;  sum.y = y + rhs.y;  sum.z = z + rhs.z;
No copy constructor.
No, see above.
I just pointed out how you changed the behavior of operator+(), which of course changes the behavior of your program.
No, it's not the same. The data members x, y, and z are qualitatively different from the member you added - name.

What are the types of x, y, and z?
What is the type of name?

Some questions I asked in my previous post:
What do you think happens with name=rhs.name;? Hint: It's not the same as, say, x = rhs.x; .
Regarding the assignment statement above, if the name field of the rhs object is allocated on the heap, what do you think happens to rhs.name when rhs goes out of scope (and is destroyed)?

You mean I have to do strncpy_(name, strlen(rhs.name) + 1, rhs.name, strlen(rhs.name) + 1) ?

Still giving me error. But if this is the right direction, I can work on it.Thanks
 
  • #22
Say you have a house, and it has an address. You can give someone a copy of your address (shallow copy), or you can make then an actually copy of your house (deep copy).

If you have your house demolished, the address you gave will be an address to a demolished house.

In other words, shallow copy is like "Mi casa es tu casa". Deep copy is like, I built you a house that's just like mine.

Now vec::name is a memory address. You set name=temp.name (shallow copy). When temp is destroyed, the memory it held is now garbage. So name, is now a memory address to garbage.
 
Last edited:
  • Like
Likes yungman
  • #23
Jarvis323 said:
You set name=temp.name (shallow copy). When temp is destroyed, the memory it held is now garbage. So name, is now a memory address to garbage.
I was hoping that @yungman would reach this conclusion on his own...
 
  • Like
Likes Vanadium 50
  • #24
yungman said:
It should be exactly the same as x = rhs.x; y = rhs.y in line 44 of the header file. I declared char* name in the public of the class definition. So the class has x, y and name as member variables.
Suppose you have this code.
C++:
double x = 3.4;
double y = 1.7;
char name[] = "First";

Given the above, is there anything wrong with the following code?
C++:
x = 6.2;
y = 8.3;
name = "Second";
 
  • #25
Jarvis323 said:
Say you have a house, and it has an address. You can give someone a copy of your address (shallow copy), or you can make then an actually copy of your house (deep copy).

If you have your house demolished, the address you gave will be an address to a demolished house.

In other words, shallow copy is like "Mi casa es tu casa". Deep copy is like, I built you a house that's just like mine.

Now vec::name is a memory address. You set name=temp.name (shallow copy). When temp is destroyed, the memory it held is now garbage. So name, is now a memory address to garbage.
Thanks for the reply.

But I already show Temp was destroy after I got the garbage. You can see clearly in post 17, Temp is destroyed in the second to the last line, I got garbage the line before that. Temp was not destroy at the time. Can you explain that with shallow copy?

I am looking into what I did wrong with copying c-string. I was wrong on name = rhs.name. to copy c-string. It has to be strncpy_s(name, size, rhs.name, size). But there is a problem with c-string inside class definition I still don't understand. I am also looking at using std::string.

Thanks
 
  • #26
Mark44 said:
Suppose you have this code.
C++:
double x = 3.4;
double y = 1.7;
char name[] = "First";

Given the above, is there anything wrong with the following code?
C++:
x = 6.2;
y = 8.3;
name = "Second";
I got that, thanks, I have to use strncpy_s().

I was working on this last night, I have issue with defining c-string inside class. I want to show two separate programs. The first one is just a simple copying c-string inside main, It works perfectly, I can copy from name2 to name1:
C++:
//Function for strings
#include <iostream>
#include<cstring>
using namespace std;

int main()
{
    const int size = 25;
    char name1[size] ;
    char name2[size] = "Nancy";
    strncpy_s(name1, size, name2, size);
    cout << name1 << "\n\n";
    return 0;

}
But when I put it inside class definition, it flagged me error right from the beginning. I cannot even go any further. Can you help me on this first. This is the header file. I can see red wiggle line under "size", this is the same as in the main above, it just doesn't work inside class definition.
C++:
#ifndef OverLoad_H
#define OverLoad_H
#include <iostream>
#include <cstring>
using namespace std;
class Vec
{private:   double x, y; const int size = 25;
public:
     Vec(char*desc) { char name1[size] = "Alan"; char name2[size]; }

};
#endif
I am not discounting what you and Jarvis323 talking about the swallow copy, I already see the problem of copying c-string, I want to see this through first.

Thanks
 
  • #27
yungman said:
I can see red wiggle line under "size",
Which one? If I add a member named size to the class that jtbell wrote, the program compiles and runs with no problems.
C++:
class ThreeVector
{
private:
    double x, y, z;
    const int size = 25;
    int serialNum;          // unique for each ThreeVector instance
    static int numVectors;  // counts the number of ThreeVectors constructed
Is the red squiggly line in your constructor that takes a string argument? If so, hover over the squiggly part and VS will give you a clue as to what's wrong.
 
  • #28
Mark44 said:
Which one? If I add a member named size to the class that jtbell wrote, the program compiles and runs with no problems.
C++:
class ThreeVector
{
private:
    double x, y, z;
    const int size = 25;
    int serialNum;          // unique for each ThreeVector instance
    static int numVectors;  // counts the number of ThreeVectors constructed
Is the red squiggly line in your constructor that takes a string argument? If so, hover over the squiggly part and VS will give you a clue as to what's wrong.
This is the picture, I have to take a picture with camera to show what the message said having the arrow on the red wiggle.
Size error.jpg


I so wish I can get pass this and at least do my best to learn about c-string passing into the class object. There should be no reason to use name = new char[] every time that I have to delete name[].

This is an example I really want to do, I just wrote one Constructor as an example:
C++:
#ifndef OverLoad_H
#define OverLoad_H
#include <iostream>
#include <cstring>
using namespace std;
class Vec
{private:   double x, y; const int size = 25;
 public:
     char name[size];
     Vec(char* desc)
       { strncpy_s(name, size, desc, size);
         x = 0; y = 0;
       }
 };
#endif
Now, I am not ignoring what you and Jarvis323 on the shallow copy, I see a problem that I feel I should learn first. I am even going to try using std::string to verify it has a constant size. Remember in my Directory program, I have a long array of structures store in file. I cannot have each structure element having different sizes as when reading file, it doesn't care about what element, structure, it literally go to that address to read certain amount of bytes. Each element has to have the same size to do that, or else it would be very hard to do.

I am running out of idea on this red wiggle error thing. I tried click search online, I don't understand what they are talking. Please help.

Thanks
 
Last edited:
  • #29
@yungman

You post a code asking what is the error. We tell you what the error is and how to fix it. You remain skeptical, don't fix the error, and instead start trying other stuff.

At the very least correct the errors you have (in this case shallow copy). It will be a waste of time debugging code full of known errors.
yungman said:
I am even going to try using std::string to verify it has a constant size. Remember in my Directory program, I have a long array of structures store in file. I cannot have each structure element having different sizes as when reading file, it doesn't care about what element, structure, it literally go to that address to read certain amount of bytes. Each element has to have the same size to do that, or else it would be very hard to do.
Unfortunately you will not be able to use a string for your use case. The reason, yes a string is a fixed size, just like vec::name has a fixed size. The size of the data they reference is something else.

Writing a string in binary directly, would be making a shallow copy of it onto the disk, when again you need a deep copy.

Understanding deep and shallow copy is still the key.

Note that string is something like this.

Code:
struct MyString{
    char *;
    int size;
};

Now an array that is NOT dynamic is ok for what you want. So you can use char name[N], and you'll be fine. But not string or char * name = new char[N].

See my second post for an example.
 
Last edited:
  • Like
Likes Vanadium 50
  • #30
Jarvis323 said:
@yungman

You post a code asking what is the error. We tell you what the error is and how to fix it. You remain skeptical, don't fix the error, and instead start trying other stuff.

At the very least correct the errors you have (in this case shallow copy). It will be a waste of time debugging code full of known errors.

Unfortunately you will not be able to use a string for your use case. The reason, yes a string is a fixed size, just like vec::name has a fixed size. The size of the data they reference is something else.

Writing a string in binary directly, would be making a shallow copy of it onto the disk, when again you need a deep copy.

Understanding deep and shallow copy is still the key.

Note that string is something like this.

Code:
struct MyString{
    char *;
    int size;
};

Now an array that is NOT dynamic is ok for what you want. So you can use char name[N], and you'll be fine. But not string or char * name = new char[N].

See my second post for an example.
Thanks for the reply. I am working on this also. The way I can think of to avoid shallow copy is actually declare sum outside of the operator+() so sum is not destroyed when exiting the function. I am experimenting with this and I have no luck so far. Please understand I am just learning, this is quite a bit above my knowledge, I am kind of blind guess here. I need a little more literal help. I can assure you the book talk nothing of these stuffs. You think I should wait to learn these later when I am ready.

Tell me whether I am on the right track.

Thanks
 
  • #31
I have been working on making a deep copy of sum in operator+() by putting sum outside of the function so it doesn't not get destroyed when exit the function. I still run into problems I tested using strncpy_s for object First, Second works and display accordingly. but the problem is with operator+(). I don't know any better how to fix it. Please give me some guidance. This is really beyond my knowledge, it's hard to take a hint at this point.

Here is my work, I made it as short as possible for easy reading.

This is main:
C++:
#include <iostream>
#include "OverLoad.h"
#include <cstring>
using namespace std;

int main()
{ const int size = 25; char Ara[size] = "First", Arb[size] = "Second", Arc[size] = "Result";
    Vec First(Ara, 1, 2);
    cout << " In main,  " << First.name << "(" << First.x << "," << First.y << ")\n\n";

    Vec Second(Arb, 3, 4);
    cout << " In main,  " << Second.name << "(" << Second.x << "," << Second.y << ")\n\n";
    Vec Result(Arc);
    cout << " In main,  " << Result.name << "(" << Result.x << "," << Result.y << ")\n\n";
// Cannot use overload =
    Result = First + Second;
 //Cannot read Result.x, Result.y
    cout << " (" Result.x << "," << Result.y << ") = (" << First.x << "," << First.y <<
        ") = (" << Second.x << "," << Second.y << ") \n\n";
    return 0;
}

Here is the header file:
C++:
#ifndef OverLoad_H
#define OverLoad_H
#include <iostream>
#include <cstring>
using namespace std;
class Vec
{  
public: 
     double x, y; 
     char sumName[25] = "sum"; char name[25];
     Vec sum() { x = 0; y = 0; strncpy_s(name, 25, sumName, 25); }//OK, declaring Vec sum.
     Vec(char* desc)//work
        { x = 0;  y = 0; 
          strncpy_s(name, 25, desc, 25);
        }
     Vec(char* desc, double x0, double y0)//work
        { x = x0;  y = y0; 
          strncpy_s(name, 25, desc, 25);//Copy First or Second into name.
        }
     void operator+ (const Vec& rhs) const//sum is public, no need to return
        { sum.x = x + rhs.x;  sum.y = y + rhs.y;} //Problem using sum
     Vec &operator=(Vec &right)
        {x = right.x; y = right.y; return *this;}
};
#endif

This is the error list:
Error.jpg


Please advice whether this is the right way to have deep copy.

Thanks
 
  • #32
You can do this:

C:
Vec operator+ (const Vec& rhs) const {
    return Vec( "sum", x + rhs.x, y + rhs.y );
}

Which is really just a simplification of this,

C:
Vec operator+ (const Vec& rhs) const {
    Vec sum(  "sum", x + rhs.x, y + rhs.y );
    return sum;
}

And you can use std::string as a helper, to make life easier if you want,

C:
Vec operator+ (const Vec& rhs) const {
    return Vec(
        ( std::string( name ) + " + " + std::string( rhs.name ) ).c_str() ,
         x + rhs.x,
         y + rhs.y );
}

Note it is returning a Vec by value, which is crucial. You wouldn't want to return a temporary object by address/reference for reasons already explained.

While the returned Vec will go out of scope, it will be first passed into the copy constructor, or operator= function, of the object that you assign it to, or be used in an expression in some other way.

For example,

C:
Vec v( "v", 0.0, 1.0 );
Vec u ( "u", 1.0, 0.9 );

Vec w = v + u;
Vec w( v + u  ); // above is short for this, since = was used during instantiation copy constructor is used
Vec w( v.operator+( u ) ); // above is short for this
// result of operator+ goes directly into copy constructor, where it is copied before it is destroyed

Vec w;
w = v + u; // in this case, since w is assigned after instantiation, operator= will be called
w.operator=( v.operator+( u ) ); // this is equivalent to above
// result of operator+ goes directly into operator=, where it is copied before it is destroyed

Also, you forgot to copy the name in operator=, and also it is advised to check for self assignment in operator=.

C:
Vec &operator=(const Vec &right) {
    if ( this != &other ) {
        x = right.x;
        y = right.y;
        strncpy_s( name, 25, right.name, 25);
    }
    return *this;
}

For cases where you had a dynamic array (pointer to memory allocated with new), you can see how to do the deep copy in this link.

https://en.cppreference.com/w/cpp/language/operators

And like Mark pointed out below, get rid of your sum() member function.
 
Last edited:
  • #33
From post #31, Source.cpp:
C++:
// Cannot use overload =
    Result = First + Second;
Your comment above misses the point. The expression on the right of the assignment operator assumes that you have a valid operator+(), that returns a Vec object. You don't, since your implementation, shown below, returns void. IOW, your implementation doesn't return anything at all.

Since you haven't overloaded the + operator, the expression First + Second is a syntax error.

From the same post, Overload.h:
C++:
Vec sum() { x = 0; y = 0; strncpy_s(name, 25, sumName, 25); }//OK, declaring Vec sum.
.
.
.
void operator+ (const Vec& rhs) const//sum is public, no need to return
        { sum.x = x + rhs.x;  sum.y = y + rhs.y;} //Problem using sum
What's worse, you have a function member named sum(), but your operator+() seems to be using sum as if it were a Vec object. This is also wrong.
 
  • #34
Guys, I have to get back to all these, I was searching on web and youtube, now I know what is shallow copy, you put the stuffs on stack, sounds like deep copy needs to put on heap. Is stack the same as the memory for push and pop stack in assembly language?

I do NOT know all these, that's why I was so lost when you guys tried to explain. I am still going to watch a little more on that first and I'll be back. These are all new to me. More new terms and names for me!

I'll be back in a little. Thanks both of you a lot, I don't want to waste any more of your time at this moment,

Thanks a million.Edit:
I did not even know operator=() is called assignment operator! I was searching and searching, couldn't find that until I did a wild guess after reading the book. The book didn't even use the name assignment, just one sentence about assigning a value that lead me to try that!
 
  • #35
yungman said:
Guys, I have to get back to all these, I was searching on web and youtube, now I know what is shallow copy, you put the stuffs on stack, sounds like deep copy needs to put on heap. ...
No. The stack is where memory is allocated for the local variables in a function. When a function is called, the stack is created to make that space, and when the function returns, the stack destroyed. So anything on the stack exists only inside of that function when it is called until it returns.

The heap is a global area to store things. You allocate some of it to use with new, for example, and then it stays allocated until you call delete. You access that memory through a pointer.

Deep copy is just a general concept. It means don't copy the address, copy what the address is pointing to. You do that when it makes sense to do. If you want to just have two copies of the address to the same memory on the heap, then that's what you do. If you want to have two objects each with their own copy of that memory, then the second object needs to allocate memory and copy the data over to it's own memory. It's like either two people sharing one house, or two people each with their own houses.

Any time you do assignment with a primitive c++ type (int, char, int*, char*, etc.), it is a straightforward copy of the value. But the pointer's value is an address. So, if copying the pointer isn't what you want, you need to go to that address and grab the stuff stored there to copy over.
 
Last edited:
  • Like
Likes yungman

Similar threads

  • Programming and Computer Science
3
Replies
89
Views
4K
  • Programming and Computer Science
Replies
4
Views
1K
  • Programming and Computer Science
Replies
23
Views
2K
Replies
10
Views
961
  • Programming and Computer Science
2
Replies
66
Views
4K
  • Programming and Computer Science
2
Replies
36
Views
2K
  • Programming and Computer Science
3
Replies
75
Views
4K
  • Programming and Computer Science
Replies
5
Views
885
  • Programming and Computer Science
Replies
30
Views
2K
Replies
10
Views
2K
Back
Top