I am still not quite fully understanding Abstract Base Classes. This is what I have created for my assignment and it seems to mostly work except that it won't return anything that was added through the derived classes. Whenever I choose hasDMA or lacksDMA I don't get the result returned from color or style.
Main.cpp
#include <iostream>
#include <conio.h>
#include "DMA.h"
using namespace std;
const int RECORDS = 1;
const int LEN = 40;
int main()
{
ABC * p_records[RECORDS];
int i;
for (i = 0; i < RECORDS; i++)
{
char temp[LEN];
int temprate;
char choice;
cout << "\nEnter label name: ";
cin.getline(temp, LEN);
cout << "Enter Rating: ";
cin >> temprate;
cout << "Enter 1 for lacksDMA or 2 for hasDMA: ";
while (cin >> choice && (choice != '1' && choice != '2'))
cout << "Enter 1, 2: ";
if (choice == '1')
{
char tempcolor[LEN];
cout << "Enter the color: ";
cin.getline(tempcolor, LEN);
p_records[i] = new lacksDMA(temp, temprate, tempcolor);
}
else
{
char tempstyle[LEN];
cout << "Enter the style: ";
cin.getline(tempstyle, LEN);
p_records[i] = new hasDMA(tempstyle, temp, temprate);
}
while (cin.get() != '\n')
continue;
}
cout << endl;
for (i = 0; i < RECORDS; i++)
{
p_records[i]->View();
cout << endl;
}
for (i = 0; i < RECORDS; i++)
{
delete p_records[i];
}
cout << "\nPress any key to continue...";
cin.sync();
_getch();
return 0;
}
DMA.cpp
#include "DMA.h"
using namespace std;
ABC::ABC(const char * l, int r)
{
label = new char [strlen(l) + 1];
strcpy(label, l);
rating = r;
}
ABC::ABC(const ABC & rs)
{
label = new char[strlen(rs.label) + 1];
strcpy(label, rs.label);
rating = rs.rating;
}
ABC::~ABC()
{
}
ABC & ABC::operator=(const ABC & rs)
{
if (this == &rs)
return *this;
delete [] label;
label = new char[strlen(rs.label) + 1];
strcpy(label, rs.label);
rating = rs.rating;
return *this;
}
ostream & operator<<(ostream & os, const ABC & rs)
{
rs.View();
return os;
}
void ABC::View() const
{
cout << "\nLabel: " << label << endl;
cout << "Rating: " << rating << endl;
}
baseDMA::baseDMA(const char * l, int r) : ABC(l,r)
{
}
lacksDMA::lacksDMA(const char * l, int r, const char * c) : ABC(l,r)
{
strncpy(color, c, 39);
color[39] = '\0';
}
lacksDMA::lacksDMA(const ABC &rs, const char * c) : ABC(rs)
{
strncpy(color, c, 39);
color[39] = '\0';
}
void lacksDMA::View() const
{
ABC::View();
cout << "Color: " << color << endl;
}
hasDMA::hasDMA(const char * s, const char * l, int r) : ABC(l,r)
{
style = new char [strlen(s) + 1];
strcpy(style, s);
}
hasDMA::hasDMA(const char * s, const ABC & rs) : ABC(rs)
{
style = new char [strlen(s) + 1];
strcpy(style, s);
}
hasDMA::hasDMA(const hasDMA & hs) : ABC(hs)
{
style = new char [strlen(hs.style) + 1];
strcpy(style, hs.style);
}
hasDMA::~hasDMA()
{
delete [] style;
}
void hasDMA::View() const
{
ABC::View();
cout << "Style: " << style << endl;
}
DMA.h
#ifndef DMA_H_
#define DMA_H_
#include <iostream>
using namespace std;
// Abstract Base Class
class ABC
{
private:
char * label;
int rating;
public:
ABC(const char * l = "null", int r = 0);
ABC(const ABC & rs);
virtual ~ABC() = 0;
virtual ABC & operator=(const ABC & rs);
virtual void View() const;
friend ostream & operator<<(ostream & os, const ABC & rs);
};
// Former Base Class Using DMA
class baseDMA: public ABC
{
private:
public:
baseDMA(const char * l = "null", int r = 0);
};
// derived class without DMA
// no destructor needed
// uses implicit copy constructor
// uses implicit assignment operator
class lacksDMA : public ABC
{
private:
char color[40];
public:
lacksDMA(const char * l = "null", int r = 0, const char * c = "blank");
lacksDMA(const ABC & rs, const char * c);
virtual void View() const;
};
// derived class with DMA
class hasDMA : public ABC
{
private:
char * style;
public:
hasDMA(const char * s = "none", const char * l = "null", int r = 0);
hasDMA(const char * s, const ABC & rs);
hasDMA(const hasDMA & hs);
~hasDMA();
hasDMA & operator = (const hasDMA & rs);
void View() const;
};
#endif
In the comments you said that the string "Color:" is displayed, but the examaple input "red" is not displayed. Because "Color:" is displayed this is not an issue of something failing in the abstract/virtual part of the code (otherwise even "Color:" would not be displayed).
The fail is in the input. When you read the choice of '1' or '2' you read only that one character, but the user actually entered extra new line as well, and so next cin.getline
will consume this extra new line and return an empty string. You should stick with the cin.getline
for all the input, even if the input is just one character, and even if the input is a number. It is easier to never use cin >>
, and handle the errors in input by yourself than to mix operator>>
with getline
and then handle all border cases when they don't play nice together.
Altough this is not part of the answer, here are some general advices:
1) In C++ avoid new char[x]
whenever possible. Use std::string
for strings and std::vector<char>
for very rare cases when std::string
is not good enough.
1b) You forgot to delete [] label
in destructor of ABC
. If it was a std::string
you wouldn't have to worry about these things.
2) It is okay to use namespace std
in source files (.cpp), and only after all the #includes
, but never use it in header files (.h). Never! Use full names in header files (like std::string
instead of string
)
EDIT: So, some code example is required. Like I said, stick with cin.getline
, and avoid operator>>
, even if the input is just one character, and even if the input is an integer:
Replace line cin >> temprate;
with this:
cin.getline(temp, LEN);
temprate = strtol(temp, NULL, 10);
Replace two lines of while
loop
while (cin >> choice && (choice != '1' && choice != '2'))
cout << "Enter 1, 2: ";
with these lines of for
with exit in the middle (my preferred way of endless looping):
for (;;)
{
cin.getline(temp, LEN);
choice = temp[0];
if (choice == '1' || choice == '2')
break;
cout << "Enter 1, 2: ";
}