RunUO Community

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

C++ Arrays

milt

Knight
C++ Arrays

Okay, so I know that in C#, I can create a class with a custom constructor, and then make an array of that class and call the constructors in the initialization. This may sound confusing, so I'll give an example.
Code:
public class Class1
{
     public string string1;

     public Class1(string s)
     {
          string1 = s;
     }
}

public class Class2
{
     public Class2()
     {
          Class1[] classes = new Class1[]
          {
               new Class1("Hello"), //Calling custom constructors in initialization of array
               new Class1("Goodbye")
          };
     }
}
What I am trying to do, is something similar in C++.
Code:
class Class1
{
     public:
          string string1;
          Class1(string s)
          {
               string1 = s;
          }
}
class Class2
{
     public:
          Class1 *classes;
          Class2()
          {
               classes = new Class1[] //Syntax error
               {
                    Class1("Hello"),
                    Class2("Goodbye")
               };
          }
          ~Class2()
          {
               delete [] classes;
          }
}
}

That C++ code gives me syntax errors in the initialization of "classes". What would be the best way to do what I am trying to do? I can't really seem to find a solution, so any help is appreciated.

Thanks
 

noobie

Wanderer
try:

Class1 * classes=new Class1[2];

classes[0]=new Class1("asds");
classes[1]=new Class1("sads");
 

milt

Knight
Well, now that I think of it... when you use the keyword 'new', it is allocating memory, so I guess at first it wouldn't know how much to allocate unless you specify the size inside the []. Am I corect?
 

arul

Sorceror
milt said:
Is it possible to use a pointer variable for this?
This is the only way I can think off atm.

Code:
Class1 *classes = (Class1 *) calloc( 2, sizeof(Class1) );

classes[0] = Class1("hey");
classes[1] = Class1("heeey");
 

noobie

Wanderer
yes, it is called dynamic allocation and if you use arul's (first) example, memory is allocated in compile time.

if you use C++, get used to pointers.. :)
 

noobie

Wanderer
arul : if you are gonna do it in C-style, it is better to use malloc rather than calloc..

anyway, C is annoying, go with C++ :)
 

Nochte

Wanderer
an array and a pointer variable are basically synonymous terms.

by
Code:
Class1 classes[] = { Class1("Hello"), Class1("Goodbye") };
You are creating a pointer pointing at the first element of classes. In effect, you can use classes exactly like you would any other pointer.

noobie said:
anyway, C is annoying, go with C++ :)
I agree..*gibbily gibbily gibbily*
 

Sep102

Page
Technically, about the best way to do what you want to do is using malloc() with placement-new.

Code:
Class1 *classes = static_cast<Class1 *>(malloc(sizeof(Class1) * 2));

new(&classes[0]) Class1("Hello");
new(&classes[1]) Class1("Goodbye");
(By the way, in doing this, you're basically duplicating the functionality of new[]. The only difference being that you're avoiding the effort of default initializing the instances when you're going to be initializing them with a different constructor directly afterwards.)

This is, technically, the best way to do it as it avoids any unnecessary temporaries or initialization that the other examples had (of course, this is assuming the optimizer doesn't just get rid of them completely, which it may very well do). Of course, all of the other ways are easier to understand, thus they have their own merits (Except for noobie's of course, which leaks memory, if it were even valid to assign a Class1 * to a Class1 at all :)).

However, as it is, none of the examples, which have been using milt's original Class1 definition, have been correct, other than mine and arul's. This is because when milt defined a constructor for Class1 that took a std::string parameter, he hid the default constructor for Class1, thus using new to create multiple Class1 objects is illegal as it requires Class1 to have a default constructor to call on each of the objects. Using malloc() (or calloc() for that matter) elides this restriction, as it doesn't call any constructors, it only allocates memory (including calloc(), though it does zero memory out for you).

Also, in arul's method, classes is a pointer variable, it just doesn't look like it. In C/C++, all arrays decay to pointer's, thus classes is usable in (almost) any place where a pointer would be allowed.

edit: Darn, Nochte beat me to my last statement.
 

milt

Knight
Code:
class Calendar
{
public:
	int year;
	int month;
	Month *months;

	Calendar()
	{
		Month table[] =
		{
			Month("January", 31),
			Month("February", 28),
			Month("March", 31),
			Month("April", 30),
			Month("May", 31),
			Month("June", 30),
			Month("July", 31),
			Month("August", 31),
			Month("September", 30),
			Month("October", 31),
			Month("November", 30),
			Month("December", 31)
		};

		months = &table; //Line of error
	}

	~Calendar()
	{
		delete [] months;
	}
}

c:\Documents and Settings\JoshuaT\My Documents\Visual Studio Projects\Calendar\Calendar.cpp(52): error C2440: '=' : cannot convert from 'Month (*__w64 )[12]' to 'Month *'

I wanted to try it this way, but for some reason I keep getting that error. Shouldn't it technically work?
 

punt59

Wanderer
If I understand you correctly, you want to initliaze a class with a string?

Code:
#include <string>
class Class1
{
public:
     Class1(const std::string &="")

public:
    std::string s;
};
And the implementation

Code:
Class1::Class1(const std::string &var)
{
    s= var;
}
 

Sep102

Page
If this were C, then yes, but C++ has stricter typing than C does, so no. What you're doing in this case is tring to assign a Month ** to a Month *. table in this case is a Month[12] or array of 12 Month objects, taking the address of that with (&) returns a pointer to a pointer to a Month, which, like I said, can't be assigned to a Month *.

Try:
Code:
months = &table[0];
Which assigns the address of the first element of table to months.

However, this code won't work as you think. You're trying to keep table around in memory for the life of the Calendar instance, however, using Month table[] = {...} is not the same thing as using Month *table = new Month[12] even though they're both pointers. table, as it is, sits on the stack for the duration of the constructor, then is automatically cleaned up, like any local variable is. Objects created using new or memory allocated using malloc() needs to be free'd or delete'd by you, which is why they stick around for as long as you want them to.

What you need to do is create table using new or malloc(), then initialize each element in it, then assign it to months, as then it will live on the heap, you can delete[] (or free()) it fine, and everything will be right in the world.
 

milt

Knight
Okay, so basically I'm stuck with this?

Code:
class Calendar
{
public:
	int year;
	int month;
	Month *months;

	Calendar()
	{
		months = new Month[12];

		months[0] = Month("January", 31);
		months[1] = Month("February", 28);
		months[2] = Month("March", 31);
		months[3] = Month("April", 30);
		months[4] = Month("May", 31);
		months[5] = Month("June", 30);
		months[6] = Month("July", 31);
		months[7] = Month("August", 31);
		months[8] = Month("September", 30);
		months[9] = Month("October", 31);
		months[10] = Month("November", 30);
		months[11] = Month("December", 31);
	}

	~Calendar()
	{
		delete [] months;
	}
}
 

Sep102

Page
Pretty much, I can give you a "possibly" more appealing way that uses std::copy from the algorithm header, but that's about it.

Code:
#include <algorithm>

/*...*/

Calendar()
{
	Month table[] =
	{
		Month("January", 31),
		Month("February", 28),
		Month("March", 31),
		Month("April", 30),
		Month("May", 31),
		Month("June", 30),
		Month("July", 31),
		Month("August", 31),
		Month("September", 30),
		Month("October", 31),
		Month("November", 30),
		Month("December", 31)
	};
	
	months = new Month[12];

	std::copy(&table[0], &table[12], months);
}

std::copy copies from the iterator (pointer in this case) &table[0], or the first element of table, to one before the second argument of the function (&table[12]) or the last element of table. It copies this into the third argument, months.
 

milt

Knight
Thank you all of you for the help... I'll send you a wad of $$ when I show up Gil Bates ;).

++karma

*edit* couln't give karma to everyone, sorry :(
 

noobie

Wanderer
thats not a good example, you are wasting memory..

I would use something like this:

Code:
class Calendar{
	public:
		Month * table;
		Calendar()
		{
			table =new Month[12]; //have a default const for Month
			//initialize your private values
	
			table[0]=Month("January", 31);
			table[1]=Month("February", 28);
			table[2]=Month("March", 31);
			table[3]=Month("April", 30);
			table[4]=Month("May", 31);
			table[5]=Month("June", 30);
			table[6]=Month("July", 31);
			table[7]=Month("August", 31);
			table[8]=Month("September", 30);
			table[9]=Month("October", 31);
			table[10]=Month("November", 30);
			table[11]=Month("December", 31);
		}
		~Calendar()
		{
			delete [] table;
		}

};
 

Sep102

Page
Yes, it is wasting memory, for the duration of the constructor, then table is automatically cleaned up along with each of its entries while months sticks around with each of the entries on the heap, essentially trading "space" (constructing each month on the stack, which isn't very much of it in this case) and some time (default allocating months and copying table to months, still minuscule) for convenience.

I was trying to give an example that would use as much of milt's code as possible, had I wanted to give an optimized example, I would have, such as:
Code:
Month * table;
Calendar()
{
        // No need for default constructor and no extra work default
        // Constructing what we're about to reconstruct, yay!
	table = static_cast<Month *>(malloc(sizeof(Month) * 12));

	//initialize your private values, no temporaries here
        new(&table[0]) Month("January", 31);
	new(&table[1]) Month("February", 28);
	new(&table[2]) Month("March", 31);
	new(&table[3]) Month("April", 30);
	new(&table[4]) Month("May", 31);
	new(&table[5]) Month("June", 30);
	new(&table[6]) Month("July", 31);
	new(&table[7]) Month("August", 31);
	new(&table[8]) Month("September", 30);
	new(&table[9]) Month("October", 31);
	new(&table[10]) Month("November", 30);
	new(&table[11]) Month("December", 31);
}

~Calendar()
{
    for(Month *first = &table[0]; first != &table[12]; ++first)
    {
        first->~Month();
    }

    free(table);
}


I like the other example I gave more though, since it uses milt's code to construct the table on the stack and also looks quite a bit cleaner and easier to follow (especially from a more C# standpoint) than this or your example. Plus, you could just as easily have made table in some static const object that would contain all of the months for the life of the program that got copied to a member in Calendar each time a Calendar is constructed (if you want a mutable calendar, otherwise it could just hold a pointer to the table). All with minimal effort from what milt had above.
 

noobie

Wanderer
well, I think that wasnt hard to understand and it is the most known syntax for memory allocation in C++, so he should get used to that one.

i have never used your syntax thb. and i tried that one just out of curiosity and it gives me a memory error "just after program exit" if you use strings instead of char*. so he needs to use C-style strings and all that stuff which is really not easy for a beginner.
 

milt

Knight
Yeah, I was trying to stay basic here, as I have just started a college C++ course. I know the basic concepts of pointers and stuff, but not all of their functionality.

One of my first assignments is to make a console app that asks for a year and month, and will spit out a calendar for that month. You have to take leap years into account, etc... but that is the easy part TBH
 
Top