Music collection with lyric songs and instrumental pieces I have problems with dealing with derived classes that inherit from the same base class.

I have 2 types of songs:


Songs with lyrics that have: Title, Tags (sad, happy etc.), lyrics (one line), writer.
Instrumental song: Title, Tags (same as lyric songs), instrumentals (a string), performer, BPM.


I thought it would be a good idea to implement a base class Song, since Title and Tags are fields that are common to the both types of the songs. Then, implement the class LyricSong and InstrumentalSong that inherit from Song.

class Song
{

public:
    Song(std::string title, std::string tags);

    std::string getTitle() const { return this-&gt;_title; }
    std::string getTags() const { return this-&gt;_tags; }

    // Since i had the need in virtual functions and since all virtual functions have to be 
    // implemented, i used default string and int to be returned.
    virtual std::string getLyrics() const { return std::string(); }
    virtual std::string getLyricsBy() const { return std::string(); }
    virtual std::string getInstruments() const { return std::string(); }
    virtual std::string getPerformer() const { return std::string(); }
    virtual int getBPM() const { return 0; }

    static songContainer readSongsFromFile(std::string songsFileName);

private:
    std::string _title;
    std::string _tags;

};

//---------------------------------------------------------------------------//

class LyricSong : public Song
{

public:
    LyricSong(std::string title, std::string tags, std::string lyrics, std::string lyricsBy);

    std::string getLyrics() const { return this-&gt;_lyrics; }
    std::string getLyricsBy() const { return this-&gt;_lyricsBy; } 

private:
    std::string _lyrics;
    std::string _lyricsBy;
};

//---------------------------------------------------------------------------//    
class InstrumentalSong : public Song
{

public:
    InstrumentalSong(std::string title, std::string tags,\
                     std::string instruments, std::string performer, int BPM);

    std::string getInstruments() const { return this-&gt;_instruments; }
    std::string getPerformer() const { return this-&gt;_performer; }
    int getBPM() const { return this-&gt;_BPM; }


private:
    std::string _instruments;
    std::string _performer;
    int _BPM;
};


Issues that have arisen:


The code is cumbersome and messy.
Every time I'll need to add new song types, it will force me to modify Song (add more functions and make them virtual - Polymorphic issues).
Will there be a way to know what type of Song I work with, in case of iterating over an array of Song pointer that point to various song types?


Are there any common design principles that can help me with this?
Polymorphic classes must include a virtual destructor. Otherwise they are not truly polymorphic: you cannot delete such an instance using the pointer to the base class.

A class hierarchy should (typically) be designed according to the Liskov Substitution Principle. This means that any derived class must be substitutable for the base class: anywhere a base class is expected to work, the derived class must as well. Your hierarchy conforms to this, but I wanted to point it out.


  Will there be a way to know what type of Song I work with, in case of iterating over an array of Song pointer that point to various song types?


That's what dynamic_cast is for - it returns nullptr when the object is not of the desired type.

Song * song = ...;
auto isong = dynamic_cast&lt;InstrumentalSong*&gt;(song);
if (isong) { 
  /* it's instrumental */
  ...
}
auto lsong = dynamic_cast&lt;LyricSong*&gt;(song);
if (lsong) {
  /* it's lyric */
  ...
}


The type of isong is deduced to InstrumentalSong*, etc.

A basic question to be asked is: do you even care about a class structure? What do you do with all that song data?