-1

I use string::copy function in my copy constructor(deep copy) and when I use it, it appends some not meaningful characters to my string. Here is my main function:

#include <iostream>
#include "SimpleMusicAlbum.h"

int main(){


    MusicAlbum msc("a7x","Seize the day",2005);

    cout << msc.getMusicAlbumArtist()   <<endl;
    cout << msc.getMusicAlbumTitle()    <<endl;
    cout << msc.getMusicAlbumYear()     <<endl;

    MusicAlbum msc2(msc);
    cout << msc2.getMusicAlbumArtist()   <<endl;
    cout << msc2.getMusicAlbumTitle()    <<endl;
    cout << msc2.getMusicAlbumYear()     <<endl;
    //MusicAlbum msc3(msc);

    return 0;
}

a strange point is that when I write a7x instead of Avenged sevenfold to title, it does not append characters.

Here is the header SimpleMusicAlbum.h:

#ifndef __SIMPLE_MUSIC_ALBUM_H
#define __SIMPLE_MUSIC_ALBUM_H
#include <string>
using namespace std;
class MusicAlbum {
 public:
    MusicAlbum(const string maArtist = "",
    const string maTitle = "",
    const int maYear = 0);

    ~MusicAlbum();
    MusicAlbum(const MusicAlbum &maToCopy);

    void operator=(const MusicAlbum &right);

    string getMusicAlbumArtist();
    string getMusicAlbumTitle();
    int getMusicAlbumYear();
 private:
    string artist;
    string title;
    int year;
};
#endif

Here is SimpleMusicAlbum.cpp:

MusicAlbum::MusicAlbum(const string maArtist,
            const string maTitle,
            const int maYear){
    artist = maArtist;
    title = maTitle;
    year = maYear;

}
//the problem is here
MusicAlbum::MusicAlbum(const MusicAlbum &maToCopy){
    char artistTemp[maToCopy.artist.size()] ;
    char titleTemp[maToCopy.title.size()];

    cout << maToCopy.artist.size() << endl;

    maToCopy.artist.copy(artistTemp, maToCopy.artist.size(), 0);
    artist = artistTemp;
    maToCopy.title.copy(titleTemp,maToCopy.title.size(),0);
    title = titleTemp;
    this->year = maToCopy.year;
}

//same problem occurs here
void MusicAlbum::operator=(const MusicAlbum &right){
    char artistTemp[right.artist.size()];
    char titleTemp[right.title.size()];
    right.artist.copy(artistTemp, right.artist.size(), 0);
    artist = artistTemp;
    right.title.copy(titleTemp,right.title.size(),0);
    title = titleTemp;
    this->year = right.year;
}
//destructor
MusicAlbum::~MusicAlbum(){
    // no allocation, no destruction.
}
//methods
string MusicAlbum::getMusicAlbumArtist() {
    return artist;
}

string MusicAlbum::getMusicAlbumTitle(){
    return title;
}

int MusicAlbum::getMusicAlbumYear(){
    return year;
}
wolfenblut
  • 131
  • 1
  • 11
  • 2
    `MusicAlbum(const MusicAlbum &maToCopy); void operator=(const MusicAlbum &right);` There is no need for a user-defined copy constructor or an assignment operator in `MusicAlbum`. I suggest get rid of them, as the compiler default versions are perfectly ok to use for your `MusicAlbum` class. By providing your own version you are 1) Potentially creating bugs and 2) Potentially taking away kind of optimization the compiler could be doing. – PaulMcKenzie Nov 17 '18 at 15:19
  • Why don't you just use `std::string` everywhere? – David G Nov 17 '18 at 15:20
  • 1
    So why are you writing your own copy constructor and assignment operator? All you did was create a problem when none needed to exist. – PaulMcKenzie Nov 17 '18 at 15:25
  • I use my own copy constructor because I want to "deep copy" strings. If don't do that, every non primitive variables of msc2 will point to where msc points. So, in this case, strings will not be deep copied. The reason I do this is similar to the reason we use string1.compare(string2) method instead of str1 == str2 control. In addition, If I delete msc, then some of the contents of msc2 will also be deleted in this case. – wolfenblut Nov 17 '18 at 15:41
  • @DenizhanSoydaş "*I use my own copy constructor because I want to "deep copy" strings.*" - `std::string` has its own copy constructor and copy assignment operator, which perform deep copies for you. You do not need to do it manually. "*If don't do that, every non primitive variables of msc2 will point to where msc points*"- no, they won't. "*So, in this case, strings will not be deep copied.*" - yes, they will be. – Remy Lebeau Nov 17 '18 at 15:47
  • @DenizhanSoydaş `char artistTemp[maToCopy.artist.size()];` note that variable length arrays aren't compliant witch the c++ standard. Also you probably miss to end the strings with a `'\0'` character properly. – πάντα ῥεῖ Nov 17 '18 at 15:48
  • @DenizhanSoydaş "*The reason I do this is similar to the reason we use string1.compare(string2) method instead of str1 == str2 control.*" - which reason is that exactly?" *In addition, If I delete msc, then some of the contents of msc2 will also be deleted in this case.*" - no, they won't be. You seem to have a fundamental misunderstanding of how `std::string` actually works. – Remy Lebeau Nov 17 '18 at 15:50
  • Unrelated: `__SIMPLE_MUSIC_ALBUM_H` is a reserved identifier and should not be used. More on that here: [What are the rules about using an underscore in a C++ identifier?](https://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier). – user4581301 Nov 17 '18 at 15:51
  • @DenizhanSoydaş `std::string s1="abc"; std::string s2=s1;`. So you thought that doing this didn't make copies of the string? If so, then you missed the whole reason why `std::string` exists. – PaulMcKenzie Nov 17 '18 at 15:57
  • Last but not least, 'using nalespace std;' in a header polutes everything that includes it with the std namespace and can cause name collisions. – johnathan Nov 17 '18 at 16:15

2 Answers2

4

Standard library containers have their own copy and assignment functions. There's no need to manually copy them.

 MusicAlbum::MusicAlbum(const MusicAlbum    &maToCopy)
 {
     artist  = maToCopy.getMusicAlbumArtist();
     title = maToCopy.getMusicAlbumTitle();
     year = maToCopy.getMusicAlbumYear();   
 }

Here is a working example.

 #include<string>
 #include<iostream>

 using namespace std;

 class MusicAlbum
 {
     public:
      MusicAlbum(string Artist, string Title, int Year):artist(Artist),
      title(Title),
      year(Year)
      {}
      string getTitle(){ return title; }
      string getArtist() { return artist; }
      int getYear() { return year; }
 private:
     string artist;
     string title;
     int year;  
 };

 int main()
 {
     MusicAlbum a{"a7x","albumname",2007};
         MusicAlbum b = a;
         cout << b.getArtist() << " " << b.getTitle() << " " <<   b.getYear() << endl;
 }
johnathan
  • 2,315
  • 13
  • 20
  • I don't want to shallow copy strings, I want to deep copy them. The ones that you provided works for int without any problem but does not work for strings :( – wolfenblut Nov 17 '18 at 15:18
  • 4
    @DenizhanSoydaş • You are mistaken, those are deep copies of the strings. – Eljay Nov 17 '18 at 15:23
  • 3
    @DenizhanSoydaş -- Types such as `std::string` have correct copy semantics. There is no reason to intercept the default copying of `std::string` and put your own version in. – PaulMcKenzie Nov 17 '18 at 15:28
  • 2
    @DenizhanSoydaş read up on the [Rule of Zero](https://en.cppreference.com/w/cpp/language/rule_of_three). One of the hallmarks of a good class is it manages all aspects of any resources it owns so that users of of the class do not have to. `std::string` is Rule of Five compliant so that no one else has to be, at least with respect to manipulating a string. – user4581301 Nov 17 '18 at 15:38
0

Let's break down the MusicAlbum class to only its member variables:

class MusicAlbum 
{
    //...
    string artist;
    string title;
    int year;
};

Given these are the member variables, making copies of MusicAlbum is perfectly safe to do if you have the compiler do the copies. Thus there is no need to supply a user-defined copy constructor and assignment operator. A std::string has correct copy semantics out of the box, and an int is obviously safely copyable. So the fix is simple:

remove those functions from your class.

If however MusicAlbum contained pointers to dynamically allocated memory, or resources that need to be handled, or some other aspect that the default compiler implementation of the copy constructor and assignment operator will not correctly provide, then yes, you would write a user-defined copy constructor / assignment operator.


So what can happen if you provide the copy constructor and assignment operator functions when you don't need to provide them?

The only outcomes are:

  1. You wrote these functions correctly.
  2. You wrote these functions incorrectly.

You would think that option 1. would be ok. Maybe, but the downside is that you may have written your version inefficiently (more than likely you did). The compiler's default copy/assignment will always work correctly, and would have been written efficiently.

As for option 2., this is what is happening to your code. Rather than debugging, simply remove those functions. If you were to debug and get these functions working correctly, you then fall right back into item 1 on the list. So you really didn't gain anything, except a few minutes or hours writing and debugging functions you didn't need to write in the first place.

So don't waste your time writing functions that the compiler already provides for you. Given the member variables in your class, the compiler will always get the copies done correctly.

PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45