Luke Purnell Luke Purnell - 2 months ago 23
C++ Question

OOP Design in C++

I am trying to create a design of a JukeBox in C++ as per question 7.3 in 'Cracking The Coding Interview'.

Below is my attempt. My thought process is to have data structures in the form of classes for both a song and a CD. Then have a CD_Player which can be passed a CD as an argument.

This is causing an issue as my CD_Player does not call the constructor for CD. I guess the problem is that CD_Player references a CD object and so if a CD_Player is made before a CD then there is a problem since CD_Player does not know how to create a CD. Therefore, is there some way for CD_Player to 'depend' on CD. Thus a CD_Player can only be created if it is passed a properly formed CD object?

Please find my code below - suggestions on improvements would be very welcome!

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class song {

protected:
string song_name;
int song_length;

public:
song(string s_n, int s_l): song_name(s_n), song_length(s_l) {};
};

class CD {

protected:
string album_name;
string artist_name;
vector<song> songs;
public:
CD(string, string, vector<song>);
};

CD::CD(string a_n, string artist_name, vector<song> sngs)
{
album_name = a_n;
this->artist_name = artist_name;
songs =sngs;
};

class CD_Player {

CD cd_inside;
public:
CD_Player() {
cout << "CD_Player " << endl;
}
void insert_CD(CD cd) {
cd_inside = cd;
}
void play(void) {
cout << "playing " << endl;//album_name << " " << song_name << endl;
}
};

int main()
{
song s1("hi", 10);
vector<song> tracks;
tracks.push_back(s1);
CD c1("first album", "artist", tracks);
CD_Player cd;
cd.play();
return 0;
}

Answer

The problem is your CD_Player already contains a CD when it is created and a CD needs information to create.

A solution to this is to allow CD objects to be created blank i.e. with nothing on them by providing a default constructor (a constructor that does not need any arguments):

class CD {

    protected:
        string album_name;
        string artist_name;
        vector<song> songs;
    public:
        CD(); // default constructor (blank CD)
        CD(string, string, vector<song>);
};

CD::CD() // blank CD (no need for info)
{
}

// pre-recorded CD
CD::CD(string a_n, string artist_name, vector<song> sngs)
{
    album_name = a_n;
    this->artist_name = artist_name;
    songs =sngs;
}

Now when you create a CD_Player it starts off with a blank CD inside that you can replace with a pre-recorded CD.

Also:

A better way to initialize an object is through an initializer-list which is a special list you can provide to the constructor like this:

CD::CD(string album_name, string artist_name, vector<song> songs)
: album_name(album_name)
, artist_name(artist_name)
, songs(songs)
{
    // NOTE: nothing in the body!
}

If you initialize in the body you end up initializing twice - the members are default initialized before the body is run and then you assign them new values in the body.

Also notice that the initializer-list fixes the problem of using the same names for parameters and member variables - the compiler doesn't see anything ambiguous - it just works.