Inheritance c++

HI everyone.
Im working on assignment given in the university about inheritance, and i have a few things i dont know how to implement.
Generally, the program manages a shop. so i was requested to create PRODUCT class, and a few classes (cheese,milk,fruit.....) that inherit from PRODUCT.
now, i had to create SHOP class, two of its parameters are number of products in the shop and array of those products.
the main problem- i need to create function that add product to the shop.
after reading information about that, i wanted to do something like that
" Product** p={Cheese &c,Milk &m} " and so on.
this is what i tried to do:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
H:
#include "Product.h"


class Shop
{
private:
	char* m_storeName;
	int m_factor;
	int m_numOfProducts;
	Product **m_prod;	//pointers array from Product type.

public:
	Shop(char *storeName,int factor,int m_numOfProducts,Product **prod);
CPP:
  #define PROD 50

Shop::Shop(char *storeName,int factor,int numOfProducts,Product **prod):m_factor(factor),m_numOfProducts(0)
{
	m_storeName=new char[strlen(storeName)+1];
	strcpy(m_storeName,storeName);
	m_prod=new Product*[PROD];
}

void Shop::addProduct(Product& p)
{
	m_prod[m_numOfProducts]=&p;
	m_numOfProducts++;
}

Main (example for vegetable!):

#include "AnotherMilkProduct.h"
#include "Fruit.h"
#include "MilkDrink.h"
#include "MilkProduct.h"
#include "Yogurt.h"
#include "Vegetable.h"
#include "Shop.h"
#define NAME 20
/* TO FIX:
1. GETTING AS AN INPUT THE LOCATION WITH SPACE.
2. CHECK INPUT WITH EXEPTIONS.
3.check uniqueness of serial number.
4.
*/
int main()
{
	char *name,location[SIZE],*prodInPack,*milkName,*notDairyNames;
	int factor,choice,serial,quantity,type1,type2,area,rippen,providers,vitamins,grams,items,color1,color2,fat,increments,notDairy;
	int numOfProducts=0,i;
	Product **p=NULL;	//products array.
	cout<<"Please insert shop's name: ";
	name=new char[NAME];
	cin>>name;
	cout<<"Please insert shop's factor: ";
	cin>>factor;
	Shop shop(name,factor,numOfProducts,p);
	cout<<endl<<"1.add"<<endl<<"2.print"<<endl<<"3.calculate"<<endl;
	cin>>choice;
	while(choice!=9)	{
		switch(choice)
		{
		case 1:	{
			cout<<"Insert product's serial number, composed of 5 integers: ";
			cin>>serial;
			cout<<endl<<"Insert product's location, consist of letter and number: ";
			cin>>location;
			cout<<endl<<"Insert product's quantity(weight at stock).";
			cin>>quantity;
			cout<<endl<<"Insert product's type: 1-Agricultular 2-Package 3-Milk: ";
			cin>>type1;
			cout<<endl<<"insert product's area in the shop: 1-hidden 2-middle exposure 3- high exposure: ";
			cin>>area;
			if(type1==1)	// if agricultural product
			{
				cout<<endl<<"Insert agricultural product name: ";
				cin>>name;
				cout<<endl<<"Insert agricultural product type: 1-vegetable 2-fruit: ";
				cin>>type2;
				cout<<endl<<"Insert number of seasons it rippens: ";
				cin>>rippen;
				cout<<endl<<"Insert number of providers of this product: ";
				cin>>providers;
				if(type2==1)	// if it is vegetable
				{
					cout<<endl<<"Insert number of vitamins in this vegetable: ";
					cin>>vitamins;
					Vegetable vtmp(serial,location,quantity,type1,area,type2,name,rippen,providers,vitamins);
					shop.addProduct(vtmp);
				}
///////////// and case 2 for printing:
	case 2:
		{
			for(i=0;i<numOfProducts;i++)
				p[i]->print();
		}


Can you please tell me how to fix that?
thanks !
I understand that a project for school may have certain restrictions. I must ask, therefore, that since you are writing a C++ application if you are restricted from using C++ standard library classes like std::string, std::vector and others.

I ask because your storage strategies are distinctly C oriented. I'm a developer of many decades experience. I wrote code for years before C++ existed, in C, and I'm familiar (though it's happily a distant memory ;) ) this type of storage strategy. Typical examples are found here in your example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22


class Shop
{
private:
	char* m_storeName;
	int m_factor;
	int m_numOfProducts;
	Product **m_prod;	//pointers array from Product type.

public:
	Shop(char *storeName,int factor,int m_numOfProducts,Product **prod);
};


Shop::Shop(char *storeName,int factor,int numOfProducts,Product **prod):m_factor(factor),m_numOfProducts(0)
{
	m_storeName=new char[strlen(storeName)+1];
	strcpy(m_storeName,storeName);
	m_prod=new Product*[PROD];
}


The use of strcpy, for example, is a C library function. A C++ programmer actively refuses to use this function in particular, but the overall concept of allocating storage for a string or other containers generally.

The same is more deeply true of m_prod. These types of storage strategies, once the mainstay of C (and of entire operating systems, including UNIX) is now widely recognized as a major source of bugs, and a waste of mental energy in professional work.

That said, even the most prejudiced C++ developers (I'm not QUITE of that description) recognize the occasion when this type of storage management is unavoidable, and for library authors may even be required. I find it uncomfortable to find a modern academic course on the subject mixing the paradigms, while at the same time I recognize why it may be so.

For example, The above code, focused just on the name of the shop, is much safer and easier written this way:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17


class Shop
{
private:
	std::string m_storeName;

public:
	Shop( const char *storeName );
};


Shop::Shop( const char *storeName )
{
	m_storeName = storeName;
}


While I'm not suggesting the removal of the other members, there are two points I'm making here. First, the std::string class is many times more convenient and safer. Storage is automatically managed. The usage of the const specifier allows the constructor to work in a few more situations (though that has limited necessity in a singleton class, like Shop might be), and - just as important - the std::string m_storeName will automatically delete the string storage at the proper time. The classic mistake made in C style storage is to forget to delete the string storage, leaving a memory leak, or deleting storage prematurely, causing a crash. Neither can happen with std::string (except under tortured, contrived examples of sophistry).

Further, if an STL container is used for product storage, the same power of automatic management is provided for the product database as is provided for the string. For that reason, the parameters you specified for shop indicating product count wouldn't be required. Most containers automatically expand storage as it's used, so you wouldn't have to specify storage before the shop is created.

I'd personally expect a std::map as product storage, but that's a slightly more advanced container which provides keyed lookup (you could find products by an id or some other identifier). You might do as well with std::vector as a rather direct replacement of the m_prod array.

If your reason for choosing the array is not because of an academic restriction, but because of limited familiarity with STL, such that you're opting for character arrays and product arrays out of a sense of familiarity, let me point out that the time soaked up by debugging C style storage will likely overtake any time needed to familiarize yourself with stl storage (they're actually quite simple).

With that said, let's assume, for whatever reason, you still intend to use m_prod as it is declared in your example.

To that end, I'll focus on this function sample:

1
2
3
4
5
6
7

void Shop::addProduct(Product& p)
{
	m_prod[m_numOfProducts]=&p;
	m_numOfProducts++;
}


This would most certainly initiate the conditions which will cause a crash later. It's also an excellent example of the reason I find mixing C++ and this C style storage strategy to be really uncomfortable.

This function takes the Product parameter as a reference. That tells us nothing about how p was fashioned, and most certainly indicates the function can't assume ownership of p. However, the m_prod array was declared as Product m_prod **, indicating a pointer to an array of pointers. This function shows you clearly understand what that means, but what you've missed is the fact that p is not a pointer this array can own.

I see no code which shows how you may have called addProduct, but what you'll never see in production code is this:

1
2
3
4

Product *np = new Product(....some constructor parameters );
shop.addProduct( *np );


This might happen to work, and you obviously know you can take the address of a reference. However, this call is also valid to write:

1
2
3
4

Product np(....some constructor parameters );
shop.addProduct( np );


Yet, because this is likely inside a function, np is "on the stack". It will evaporate when the function exits. The object stored in the m_prod array would be invalid, certain to cause a crash when used.

If addProduct takes a pointer, instead, it communicates more clearly what is intended (though, it's still known to be an evil paradigm, it's C and it worked for decades, it's just not protected).

Personally, for this storage strategy, I'd prefer a function which accepts parameters describing a product (whatever those are), which is fashioned with "new Product..." inside the addProduct function.

That isolates the USER code (that which creates new products) from the detail of creating and storing the product, placing the act of allocating a new product closer to the mechanics of that storage.

Put another way, this:

1
2
3
4

Product *np = new Product(....some constructor parameters );
shop.addProduct( *np );


Puts the "new Product", a strategy of allocation and therefore of the storage, inside the application code (likely in many places), which could "dangle". What if addProduct failed? What if storage were already exhausted and it said "nope". How would you know to delete np to avoid a leak? Why would that be the caller's job?

It shouldn't be the callers job. addProduct should do that. If addProduct is going to refuse storage, it should realize there's no room and simply skip the "new Product" safely.

That being said, your storage strategy is otherwise ok. You have an array of pointers to products, and each one is to be allocated - I just don't see where you are allocating them in the sample you provided.

Now, that brings the question of deletion. Here I'm talking about how you go about closing down "Shop". When m_prod is to be deleted, this storage strategy requires that you loop through each pointer in m_prod and delete and Product, one at a time. If not, each Product would be a memory leak.

Here's where I think std::vector would be of great help. Consider:

1
2
3
4
5
6
7
// in the Shop class
std::vector< std::shared_ptr< Product > >   products;

// in some addProduct function

products.push_back( std::shared_ptr< Product >( new Product(....constructor parameters for a product ) ) );


push_back is the function used for vectors to "push a new item" into the vector. It's std::vector's version of "add...whatever". Notice, THAT IS IT!

Storage is automatically managed. There will be no memory leaks.

 
cout << products[ 2 ]->ProductName();


That little thing would print the product name of product number 3 (starting from 0).

Questions?
Last edited on
this is not complete but it should help you :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <stdio.h>

#include <malloc.h>

class Dairy
{
public:
	enum DAIRY_TYPE 
	{
		MILK = 0,
		CHEESE,
		YOGURT
	};
protected:
	DAIRY_TYPE m_type;
	const char* m_info;
	Dairy(DAIRY_TYPE _type) : m_type(_type){}
public:
	virtual ~Dairy() = 0{}
	DAIRY_TYPE getType() const
	{
		return m_type;
	}
	void print() const
	{
		fputs(m_info, stdout);
	}
};

class Milk : public virtual Dairy
{
public:
	void* operator new(size_t i)
	{
		return _aligned_malloc(i, 16);
	}
	void operator delete(void* p)
	{
		_aligned_free(p);
	}
	Milk() : Dairy(MILK) 
	{
		m_info = "I am milk !\n";
	}
	virtual ~Milk()
	{
		fputs("Destroying milk!\n",stdout);
	}
};
class Yogurt : public virtual Dairy
{
public:
	void* operator new(size_t i)
	{
		return _aligned_malloc(i, 16);
	}
	void operator delete(void* p)
	{
		_aligned_free(p);
	}
	Yogurt() : Dairy(YOGURT)
	{
		m_info = "I am yogurt !\n";
	}
	virtual ~Yogurt()
	{
		fputs("Destroying yogurt!\n", stdout);
	}
};
class Cheese : public virtual Dairy
{
public:
	void* operator new(size_t i)
	{
		return _aligned_malloc(i, 16);
	}
	void operator delete(void* p)
	{
		_aligned_free(p);
	}
	Cheese() : Dairy(CHEESE)
	{
		m_info = "I am cheese !\n";
	}
	virtual ~Cheese()
	{
		fputs("Destroying cheese!\n", stdout);
	}
};

class Market
{
private:
	static const size_t m_dairies_size = 3;
	Dairy* m_dairies[m_dairies_size];
	size_t m_iterator;
public:
	Market()
		: m_dairies()
		, m_iterator(0)
	{}
	~Market()
	{
		for (size_t i = 0; i < m_dairies_size; ++i)
		{
			delete m_dairies[i];
			m_dairies[i] = nullptr;
		}
	}
	const Dairy* getDairy(Dairy::DAIRY_TYPE _type) const
	{
		//Note that you can remove the dairy product if you want as well
		//but make sure to use another kind of iterator
		//currently the iterator is simply linear
		for (size_t i = 0; i < m_iterator; ++i)
		if (m_dairies[i]->getType() == _type)
			return m_dairies[i];
	}
	void addDairy(Dairy::DAIRY_TYPE _type)
	{
		if (m_iterator < m_dairies_size)
		{
			switch (_type)
			{
			case Dairy::MILK:
				m_dairies[m_iterator] = new Milk();
				m_iterator++;
				break;
			case Dairy::CHEESE:
				m_dairies[m_iterator] = new Cheese();
				m_iterator++;
				break;
			case Dairy::YOGURT:
				m_dairies[m_iterator] = new Yogurt();
				m_iterator++;
				break;
			default:
				break;
			}
		}
	}
};


int main()
{

	Market* market = new Market();
	market->addDairy(Dairy::MILK);
	market->addDairy(Dairy::YOGURT);
	market->addDairy(Dairy::CHEESE);
	market->getDairy(Dairy::MILK)->print();
	market->getDairy(Dairy::YOGURT)->print();
	market->getDairy(Dairy::CHEESE)->print();

	delete market;

	return 0;
}
Thanks a lot both of you! didnt expect that elaborated answers.
But your level is much higher than mine, unfortunately i didnt understand more than half you had said.. (and during restrictions of time i cant learn those subjects now).

is it possible to create that function "addProduct" with my tools (char*,no vector and so on).?
i want that function to insert the products references to the array.

thanks again
All of what was suggested in both posts can be done with any recent compiler.

However, in case you didn't realize it (I was trying to avoid giving an answer which did the work for you), the function addProduct should probably be replaced with addCheese, addYogurt, addMilk, or whatever products you create.

However, as an alternative, you could create an enum:

 
enum { Cheese, Yogurt, Milk }


Then, use that as as parameter (probably the first one) to indicate which kind of product to add, which would lend itself toward using just one function, addProduct - it's just a more complex function that way.
Topic archived. No new replies allowed.