Archived

This topic is now archived and is closed to further replies.

pdstatha

Constructor problem

Recommended Posts

pdstatha    122
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;
}  

Share this post


Link to post
Share on other sites
B-Barlow    122
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

Share this post


Link to post
Share on other sites
mdfmKoRn    122
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

Share this post


Link to post
Share on other sites
Promit    13246
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.

Share this post


Link to post
Share on other sites
B-Barlow    122
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

Share this post


Link to post
Share on other sites
Prosper/LOADED    100
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.

Share this post


Link to post
Share on other sites
merlin9x9    174
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;
};

Share this post


Link to post
Share on other sites
pdstatha    122
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

Share this post


Link to post
Share on other sites
merlin9x9    174
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 namespace


void 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 12

singleInt = 14; // ERROR: this changes what the pointer points to

a = singleProduct1.getName(); // ERROR: singleProduct is a pointer and must be deferenced

a = (*singleProduct1).getName(); // OK: singleProduct is dereferenced to get the object it points to

a = 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;
}

...
};

Share this post


Link to post
Share on other sites
merlin9x9    174
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;
}

Share this post


Link to post
Share on other sites
WayfarerX    130
In Java, boolean, byte, char, short, int, & long all go on the stack fine, but cannot go directly in the heap.

byte b; // byte on stack
int i = 80; // int on stack
long l = new long; // Error!

Everything else extends Object and goes only in the heap. Refrences to these objects (read: pointers) go on the stack.

String s1; // Refrence to a string (null)
String s2 = new String(); // Refrence pointing to String in heap
String s3("Hi"); // Error!


"So crucify the ego, before it''s far too late. To leave behind this place so negative and blind and cynical, and you will come to find that we are all one mind. Capable of all that''s imagined and all conceivable."
- Tool
------------------------------

Share this post


Link to post
Share on other sites
CaptainJester    523
quote:
Original post by merlin9x9
Ah, so then he did need all of those new s. But not in C or C++.


More precisely, in C/C++, when you declare a variable, it also has space allocated for it right away(on the stack), so if there is only one instance, then new is automatically called.

ie. MyClass test;

This allocates the memory and creates a new instance of MyClass.

In Java MyClass test; only declares the variable test is there, but sets it to null until space is specifically allocated for it by the programmer. You can do this right away or later.

ie. MyClass test=new MyClass();

or MyClass test;
.
.
.
test=new MyClass();

---
Make it work.
Make it fast.

Share this post


Link to post
Share on other sites
merlin9x9    174
Are you telling me that variable declaration results in a call to new? If so, that''s absolutely not true. Such variables have the implied auto keyword, which, as you said, results in stack-based allocation. The new operator is solely for dynamic allocation (on the heap or "free store") and is only called when you explicitly call it, just like you explicitly call malloc for dynamic allocation in C. Perhaps you were thinking of the constructor. If so, yes, the constructor gets called for both stack-allocated and dynamically-allocated (with new) objects.

Share this post


Link to post
Share on other sites
pdstatha    122
I think were almost there, this is a real opener and very informative thanks once again. There seems to be only a couple of things the compiler doesn't like now.

        
#include <iostream.h>
#include <string>
#include "classes.h"
using namespace std;

Tray *tray_prod_init() {

Tray *t_array = new Tray[10];//'Tray' : no appropriate default constructor available

//I'm presuming that I should use a similar def constructor to

//the one we used for the product???


//Set up List of Products


Product mars("Mars",40);
Product Snickers("Snickers",40);
Product Wispa("Wispa",35);
Product Aero("Aero",40);
Product Yorki("Yorki",40);
Product Galaxy("Galaxy",40);


And the second half


//Set up List of trays


Tray A1("A1",mars);
Tray A2("A2",Snickers);
Tray A3("A3",Wispa);
Tray A4("A4",Aero);
Tray A5("A5",Yorki);
Tray A6("A6",Galaxy);

//Assign position in array for each tray


Tray[0] = mars;//syntax error : missing ';' before '['

Tray[1
] = Snickers;//syntax error : missing ';' before '['

Tray[2] = Wispa;//syntax error : missing ';' before '['

Tray[3] = Aero;//syntax error : missing ';' before '['

Tray[4] = Yorki;//syntax error : missing ';' before '['

Tray[5] = Galaxy;//syntax error : missing ';' before '['


return t_array;

}

void main() {

Tray* tList = tray_prod_init(); // use tList

delete[] tList; // MAKE SURE YOU DO THIS WHEN YOU'RE DONE!!!


}

Edited by - pdstatha on January 3, 2002 8:36:11 AM

Edited by - pdstatha on January 3, 2002 8:38:04 AM

The text is there, just for some reason it's appeared in white so if you want to see it just highlight it by dragging your mouse.

Edited by - pdstatha on January 3, 2002 8:40:16 AM

Share this post


Link to post
Share on other sites
CaptainJester    523
quote:
Original post by pdstatha
Tray *t_array = new Tray[10];//'Tray' : no appropriate default constructor available
//I'm presuming that I should use a similar def constructor to
//the one we used for the product???


Yes.
quote:
Original post by pdstatha

//Assign position in array for each tray

Tray[0] = mars;//syntax error : missing ';' before '['
Tray[1] = Snickers;//syntax error : missing ';' before '['
Tray[2] = Wispa;//syntax error : missing ';' before '['
Tray[3] = Aero;//syntax error : missing ';' before '['
Tray[4] = Yorki;//syntax error : missing ';' before '['
Tray[5] = Galaxy;//syntax error : missing ';' before '['

return t_array;



A class is no different than an int or a float. If you used:
int *a=new int[10];
would you then use:
int[0]=5;
int[1]=8; .....

Replace Tray[0] with t_array[0] and you can figure out the rest.

---
Make it work.
Make it fast.

Edited by - CaptainJester on January 3, 2002 8:45:12 AM

Share this post


Link to post
Share on other sites
pdstatha    122
LOL, shit yeah stupid me updated version of that bit:

  
Tray A1("A1",mars);
Tray A2("A2",Snickers);
Tray A3("A3",Wispa);
Tray A4("A4",Aero);
Tray A5("A5",Yorki);
Tray A6("A6",Galaxy);

//Assign position in array for each tray


t_array[0] = A1;
t_array[1] = A2;
t_array[2] = A3;
t_array[3] = A4;
t_array[4] = A5;
t_array[5] = A6;


I''ve also written a default constructor for the tray:

  
Tray(const char* itsCode = " ", const Product* trayItem):
code(itsCode), prod(trayItem)
{}


I think it''s almost there the only problem being is what would be the default parameter for "const Product* trayItem" be???

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
ok here''s the issue:

you have a variable of type Product in your tray class and there is no default constructor for Product. That is ok ONLY IF you initialize Product in every tray constructor. If you don''t before you get around to initializing Product it will be in an invalid state and C++ does not allow that. So use one of those colons and put an initialization after.

The default value for a pointer is null but why are you using one anyway? Earlier you wanted to have a string and int in the constructor. Just make default parameters for those and it should work.

Share this post


Link to post
Share on other sites
krez    443
either that, or make a constructor with no parameters:
  
Product::Product(void)
{
name = new string("NULL PRoduct");
price = 0;
};

note: i don't use STL strings, so that is just a guess at how to create and initialize one.

--- krez (krezisback@aol.com)

EDIT: stupid CODE tag doesn't disable stupid smileys

Edited by - krez on January 3, 2002 2:21:48 PM

Share this post


Link to post
Share on other sites
merlin9x9    174
name = new string("Whatever") will absolutely not work since name is of type string , while new always returns a pointer to the object it creates. Plus, you'd then have to remember to use delete on name , presumably in the destructor.

If you want to initialize any object upon its construction in your constructor, just call its contructor in the class contructor initialization list:
    
class blah
{
public:
blah():
name("Hello, world"), stuff(12.0f)
{}
private:
string name;
float stuff;
};


It just so happens that both string and float have operator= overloaded, so you can also assign to them like this (what you're used to):
  
class blah
{
public:
blah()
{
name = "Hello, world";
stuff = 12.0f;
}
...
}


Although these are essentially equivalent, they're literally not. In the second example, both name and stuff have been constructed with their default constructors. So, the only way to truly "initialize" those objects as they're constructed is to call their constructors. And, of course, you can only do that when the objects themselves get constructed. But inside the body of the constructor, those objects have already been constructed, which is what initialization lists are for: to give you the chance to construct as they're constructed.

One thing that'll get you into some serious trouble, having come from a Java background, is excessive use of new . As a rule, you rarely have to use new , except for when you'll only know how many items to allocate at runtime. Otherwise, use standard static allocation. In the case of classes, structs, and unions, every member you declare is stack-allocated, so never try to use new to allocate them.

Edited by - merlin9x9 on January 4, 2002 8:26:01 AM

Share this post


Link to post
Share on other sites
pdstatha    122
Thats fine for a string, but what about for an object that has been user defined such as in my Product class. Eg.

      
class Tray{
public:
Tray(const char* itsCode = "", const Product* trayItem)://'Tray::Tray' : missing default parameter for parameter 2


code(itsCode), prod()
{}
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;
};


Obviously I have to intialise the Product in the constructor but I thought I was doing this by here.

        
): code(itsCode), prod()



Edited by - pdstatha on January 4, 2002 8:43:46 AM

Edited by - pdstatha on January 4, 2002 8:44:59 AM

Share this post


Link to post
Share on other sites
merlin9x9    174
The same applies for all types, both built-in and user-defined.

All members will be constructed no matter what (you'd never be able to use them, otherwise). In other words, you don't have to explicitly initialize/construct them using the initializer list. So, since you're using the Product 's default constructor in Tray::Tray for prod , there's no need to specify an empty argument list; you can omit prod from the initializer list altogether. This is because, again, members are constructed no matter what you do, and the only time objects' default constructors are not called is when you use one of their other constructors in an initializer list.

Here's an example. This...
    
class hello
{
public:
hello(int val = 144):
x(val)
{}
private:
int x;
};

class blah
{
public:
blah():
a(), b(12)
{}
private:
hello a;
int b;
};

...is exactly equivalent to...
  
class hello
{
public:
hello(int val = 144):
x(val)
{}
private:
int x;
};

class blah
{
public:
blah():
b(12)
{}
private:
hello a;
int b;
};


In the example, having omitted the imitialization of a with an empty argument list will result in a containing a value of 144 for its x member. If you wanted that member to be set to something other than 144—the default—on construction, then you would have to put a in the initializer list, specifying the desired value as the parameter.

Of course, you can put the empty argument list and all of that in there if it makes you more comfortable with what's going on, but it's not necessary.

Edited by - merlin9x9 on January 4, 2002 11:36:25 AM

Share this post


Link to post
Share on other sites