Constructor problem

Started by
21 comments, last by pdstatha 22 years, 3 months ago
I can''t quite figure out why I get this error??? error C2512: ''Product'' : no appropriate default constructor available
  
class Product {
public:
	Product(string itsName, int itsPrice);
	~Product() {}
	string getName() { return name; }
	int getPrice() { return price; }
private:
	string name;
	int price;
};

Product::Product(string itsName, int itsPrice) {	
	name = itsName;
	price = itsPrice;   
}

class Tray {
public:
	Tray(string itsCode,Product trayItem);
	~Tray();
	string getCode() { return code; }
	Product getProduct(){ return prod; }
	void dispense() { quantity--; }
	int getQuant() { return quantity; }
private:
	int limit;
	int quantity;
	string code;
	Product prod;
};

Tray::Tray(string itsCode,Product trayItem) { //ERROR HERE

	code = itsCode;
    prod = trayItem;
	limit = 20;
    quantity = 20;
}  
Advertisement
Try this.
        Tray::Tray(string itsCode,Product* trayItem) { //ERROR HERE    


Looks like it thinks you're passing by value, in which case it will try to create a new Product object inside the Tray constructor. Since there's no parameters being passed to the Product constructor when it makes it, it can't find an appropriate constructor.

To fix it, pass a pointer to a Product to the Tray constructor.


Edited by - B-Barlow on January 2, 2002 10:05:51 AM
You can't pass variables to a constructor. Instead, maybe have an init() function that the constructor calls?

The constructor has to be:
    Product::Product(){}    


I'm not sure if there is a way to do it, but I've never gotten it to work.

----
Herb M. (mdfmKoRn)
www.sky-meyg.com
s3202@attbi.com


Edited by - mdfmKoRn on January 2, 2002 10:06:01 AM
Both of you guys missed the point entirely.

YOu can have variables passed to a constructor just fine, and I don''t think the tray class has anything to do with this.

I can''t see directly from the code why this shouldn''t work. I have, however, on occasion noticed that if the compiler chooses not to rebuild a certain file, problems can result. The problems are particularly bad when all the function defs are in the header. Sometimes, simply deleting all the interim files (mostly *.obj) may fix the problem.

-----------------------------
The sad thing about artificial intelligence is that it lacks artifice and therefore intelligence.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
quote:Original post by Promit
Both of you guys missed the point entirely.

YOu can have variables passed to a constructor just fine, and I don''t think the tray class has anything to do with this.

I can''t see directly from the code why this shouldn''t work. I have, however, on occasion noticed that if the compiler chooses not to rebuild a certain file, problems can result. The problems are particularly bad when all the function defs are in the header. Sometimes, simply deleting all the interim files (mostly *.obj) may fix the problem.

-----------------------------
The sad thing about artificial intelligence is that it lacks artifice and therefore intelligence.



I got $5.00 says I''m right
quote:Original post by mdfmKoRn
You can''t pass variables to a constructor.


You''re wrong. You *can* pass variables to a constructor. The problem is, as B-Barlow said, pdstatha pass his object by value. Therefore the compiler try to call the default constructor but there isn''t any.

Using a pointer is a solution but a reference should be nicer (IMO) since you don''t have to change the method body (with a pointer you must add nasty * everywhere) :

  Tray::Tray(string itsCode,Product & trayItem)   


Just a note : unless you have really good reasons, *never* pass an object by value. It can be very slow with big objects. If you don''t need to change it, pass it as a const reference.
A default constructor is the constructor that either takes no arguments or has defaults supplied for all the arguments so client code doesn''t have to supply them. And, as you can see, you haven''t implemented a default constructor for your Product class. Normally, it wouldn''t be a problem...

...but, for some reason, you''re passing both strings and Products by value, and that''s the problem. Passing by value involves copying, and copying involves the default contructor being called. Simply pass by reference (const reference in this case).

Only built-in types should be passed by value. Everything else should be passed with pointers or references. Period. And, most of the time with references, they''ll be const references since you''ll have no need to modify the passed objects.

Here''s how I''d define your classes, preserving your basic interface:

  class Product{    public:        // default constructor (an added benefit is that it can        // be initalized with a string literal)        Product(const char* itsName = "", int itsPrice = 0):            name(itsName), price(0)            {}        // normal constructor        Product(const string& itsName, int itsPrice):            name(itsName), price(itsPrice)            {}        // if your destructor doesn''t need to do anything,        // you don''t need to implement one        // since these functions don''t modify the object (and shouldn''t), mark them as const        const string& getName() const {return name;}        int getPrice() const {return price;}    private:        string name;        int price;};class Tray{    public:        Tray(const string& itsCode, const Product& trayItem):            limit(20), quantity(20), code(itsCode), prod(trayItem)            {}        // I''m guessing that you don''t need a deconstructor here, either        const string& getCode() const {return code;}        const Product& getProduct() const {return prod;}        int getQuant() const {return quantity;}        void dispense() {--quantity;}private:        int limit;        int quantity;        string code;        Product prod;};  
Thanks guys that was really helpful. Now in the main program, I am wanting to create an array of products and trays in a method called tray_prod_init(). Now in java all I had to was the following.

      Tray[] tray_prod_init() {    Tray t_array[] = new Tray[10];    //Set up List of Products    Product mars = new Product("Mars",40);    Product Snickers = new Product("Snickers",40);    Product Wispa = new Product("Wispa",35);    Product Aero = new Product("Aero",40);    Product Yorki = new Product("Yorki",40);    Product Galaxy = new Product("Galaxy",40);    //Set up list of trays    Tray A1 = new Tray("A1",mars);    Tray A2 = new Tray("A2",Snickers);    Tray A3 = new Tray("A3",Wispa);    Tray A4 = new Tray("A4",Aero);    Tray A5 = new Tray("A5",Yorki);    Tray A6 = new Tray("A6",Galaxy);    t_array[0] = A1;    t_array[1] = A2;    t_array[2] = A3;    t_array[3] = A4;    t_array[4] = A5;    t_array[5] = A6;    return t_array;}    


And then to initialise it in the main method the following

  public static void main(String[] args) throws IOException {        //Initialise objects needed        Tray tList[] = new Tray[10];        tList = tray_prod_init();}    


As you can imagine C++ reacts none to kindly to this. What alterations do I need to make in order for C++ to accept it. I've tried the following but this doesn'y work either

  Tray *tray_prod_init() {	Tray *t_array[] = new Tray[10];        Product mars = new Product("Mars",40);        Tray *A1 = new Tray("A1",*mars);        t_array[0] = &A1        return *t_array;}int main() {        Tray *tList[] = new Tray[10];        tList = tray_prod_init();        return 0;}      


Edited by - pdstatha on January 2, 2002 11:01:07 AM

Edited by - pdstatha on January 2, 2002 11:02:44 AM
Before anything else, I must point out that C++ has no native string type like Java does. So, when you''re initializing your objects with string literals (places where you have stuff in quotes in your code), you''re passing a char pointer to the first character of the literal, so your function has to accept const char*—which, if you recall, I added to your code.

The closest to a string type in C++ is part of the Standard Template Library. Here''s an example:
  #include <string>using namespace std; // stuff from string is in this namespacevoid main(){    string a;    a = "Hello, world!";    a.erase(); // clears the string    int stringLength = a.length();}  


Next, Java has a garbage collector, so anything you allocate is automatically taken care of. C++ has no such facilities. So, if you use new to allocate something, you''ll have to use delete or delete[] to deallocate it. Failure to do so results in memory leaks, which is very bad. Here are some examples of proper dynamic allocation in C++:
  int* singleInt = new int;int* arrayOfInts = new int[100]Product* singleProduct1 = new Product;Product* singleProduct2 = new Product("Hello", 10);Product* arrayOfProducts = new Product[300];delete singleInt;delete[] arrayOfInts;delete singleProduct1;delete singleProduct2;delete[] arrayOfProducts;  

Make sure that you always deallocate an array with delete[] and that you always deallocate a single object with delete.

Both new and new[] (the version used to create arrays as in arrayOfInts) return a pointer to the new object, hence the int* and Product*.

You''ll probably have to do a little reading on pointers since they don''t exist in Java, and I assume that your C++ experience is limited. Here are some examples, though, using the objects in the previous example (before they''re deleted):
  *singleInt = 12; // OK: sets the object that singleInt points to to 12singleInt = 14; // ERROR: this changes what the pointer points toa = singleProduct1.getName(); // ERROR: singleProduct is a pointer and must be deferenceda = (*singleProduct1).getName(); // OK: singleProduct is dereferenced to get the object it points toa = singleProduct1->getName(); // shorthand version  


In the Java tray_prod_init you''re returning the array you allocated. This isn''t legal C++ since arrays are treated like pointers—arrays are''t true objects. In C++, since new returns a pointer to what''s allocated, tray_init will return a pointer. And since the allocation occurs in tray_prod_init, you''ll no longer need new in your main function. Instead, you just need a pointer. Try this:
  Tray* tray_prod_init(){    Tray* t_array = new Tray[10];    Product mars("Mars", 40);    Product snickers("Snickers", 40);    ...    Tray A1("A1", mars);    Tray A2("A2", snickers);    Tray[0] = mars;    Tray[1] = snickers;    ...    return t_array;}void main(){    Tray* tList = tray_prod_init();    // use tList    delete[] tList; // MAKE SURE YOU DO THIS WHEN YOU''RE DONE!!!}  


I couldn''t help noticing how much you were using new in your Java version. Since you were passing your stuff by value, I tend to think that there was no need for dynamic allocation (using new). Anyway, if you followed my (and everyone else''s) previous advice, you''re now using const references to intitialize your classes.

Since each Tray has it''s own Product object, there''s no need to allocate a Product object separately. This is true with your Java implementation as well, as I hinted in the paragraph above. Anyway, that''s why I''m just using standard, stack-allocated Product objects. Since there''s no way to initialize each new-allocated Tray object separately, you used the assignment operator to copy each Product to its associated Tray object. The same can be done in C++.

In some cases, the C++ compiler (or logic) will require you to write your own assignment operator for objects in the case that it can''t figure out how to copy or that you need more than just a "shallow" copy. Here''s how you''d do that (if you need it):

  class Tray{    public:               ...               void operator=(const Tray& trayItem)        {            // you''ll have to implement these set functions            // or (better yet) define Product in Tray so that            // you can just set the members directly            code = trayItem.getCode();            prod = trayItem.getProd();            limit = trayItem.limit;            quantity = trayItem.quantity;        }        ...};  
Forgive me if I incorrectly assumed that Java has stack-based allocation.

  void blah(){    // Is this valid Java code?    // It _is_ valid C and C++    int a;    // Or, does Java _require_ this:    int a = new int;}  

This topic is closed to new replies.

Advertisement