Quantcast

Jump to content


Constructor, Destructor, Dynamic Memory Allocation, Operator Overloading


  • Please log in to reply
15 replies to this topic

#1 Guest_jcrgirl_*

Guest_jcrgirl_*

Posted 23 October 2011 - 03:16 PM

No matter where I look on the internetz or my textbook I seriously have no concrete understanding of what the fuck these things ARE, what they DO, and how the fudge to IMPLEMENT them!
So if anyone can (in understandable english terms) help me understand what they are, I'll blow you a kiss over the internetz Posted Image
In C++ terms would be best, but I'm sure that data structures act the same in different languages too.

First off:

What is a constructor? What is a Destructor?
What is a copy constructor?
What does it do?
How do I implement one?
If I implement one, what would it look like in C++?

How do I dynamically allocate memory?
How do I know if I don't allocate enough?
What does it look like in C++?

What the FUDGE is "operator overloading"?
On my homework, it says I need to overload the assignment operator?
What the fudge does that even mean?
It sounds like I'm stuffing whipped cream into a twinky or something just waiting for it to explode, like holy shit dude...
Does this always look the same?
What is an example of it?
My professor said that whenever you implement a data structure that uses dynamic something something memory I need to overload operators? Or assign operators? what what?

Will post more questions later. Also, will perhaps include answers that seriously help me in an article that converts programming into English. I'm sure I'm not the only person in the world who (when faced with big words in textbooks) has no idea what is going on. Or maybe I'm just dumb.
Whatever the case may be... I need help so *hugs codexians*

Well if nobody can help, I guess I've got more research to do. Posted Image

#2 Sida

Sida
  • Tsvetesman

  • 3865 posts

Posted 24 October 2011 - 07:39 AM

I really don't know much C++ so I can't help much with the syntax, but I can try and explain some of them.

Constructor: This is used when an object is first created. In some languages (not sure about C++), there is something called a default constructor which allows you to create an instance (a new 'copy') of that class without having to give it any information. However, say I wanted to write a class to add 2 numbers together, we'll call it Calculator. How will this class know what numbers I want to add if I don't tell it? I can override the constructor for Calculator and allow 2 numbers to be passed to it. This means instead of calling:

new Calculator(); // this is the default constructor

I would instead call

new Calculator(5, 10); // this is my overridden constructor

I could now refer to these numbers throughout the class and use them for whatever I wanted. The name of the class with the brackets following is the constructor. Constructors can contain anything, numbers, strings, objects in any quantity or combination and you can have multiple constructors allowing you to create the same class in all kinds of different ways.

A destructor is the opposite. It's called with the class is destroyed and is used to allow you to clean up any memory that will no longer be used, for example objects that were created earlier etc. I don't know any more than this as Java does this for you :p

copy-constructors.. Remember I said a default constructor exists? It seems in C++ a default copy-constructor also exists that is specifically used if you want to make a direct copy of an object.

Say I made a had a Class called Test and I created one:

Test p;

Maybe I could do some shit to p, change a few things in it, whatever. Now if I wanted a copy of P, lets say called pCopy, I could use the default copy-constructor to do just that:

Test pTest(p);

You know how I spoke about constructors above? Well I never told pTest it could take (p) as a constructor, it just knows already. That's the default copy-constructor.

Dynamic memory...say for example I create an array. I could set the size of this array to 5 quite easily, right? But what if I wanted to log into neopets, count how many items I have and create an array of that size? I don't know the answer to this when I'm writing the program. This is an example of dynamic memory; creating an array of a certain size of which I only know once the program is running.

I haven't really had to use operator overloading either but here is my understanding of it.

Let's say you have a class called Hello. If I asked you to show me 10 plus 10 in code, you'd write

10 + 10.

That's easy for you and the computer. But what if I said show me Hello plus 10? Hello is a class, not an integer, so how will the compiler know what the hell to do with Hello + 10? It won't. It can't read your mind. If it could think, it would probably be thinking "Srsly? Wtf am I supposed to do with Hello + 10?". However you can tell it what to do with Hello + 10. That's operator overloading. You can do this with most of the standard operators (+, <, >, +=, ==, !=) etc and you will then be able to use these in conjunction with your class which wouldn't normally be the case, such as:

if(Hello == 10)
if(Hello < 1)
etc

__________________________________________________________

C++ really isn't my thing and I wrote this in a rush while leaving work, so someone please correct me where needed :p

#3 Guest_jcrgirl_*

Guest_jcrgirl_*

Posted 24 October 2011 - 10:00 AM

Thanks! That was extremely EXTREMELY helpful in regards to the overloading .. I wish all books would explain it this way sometimes >_<
I still need to figure out how dynamic memory and pointers inside of the classes work :(

I have an assignment to implement a hash table and its kind of like ... uhhhh Posted Image

#4 Sida

Sida
  • Tsvetesman

  • 3865 posts

Posted 24 October 2011 - 10:49 AM

Thanks! That was extremely EXTREMELY helpful in regards to the overloading .. I wish all books would explain it this way sometimes >_<
I still need to figure out how dynamic memory and pointers inside of the classes work :(

I have an assignment to implement a hash table and its kind of like ... uhhhh ../../public/style_emoticons/default/unsure.gif


A hash table is a lot simpler than you think. It's simply a map of keys and values. Each key has a value, and you can find out a value by using it's key.

Name / Age (Key / Value)

John : 26
Chris: 31
Dave: 22
Claire: 45

Then if you want to retrieve a value, simply pass the key(name) into the map and it'll return the value(age). Theres more to it than that, such as buckets and the actual hash function, but I assume that's out of the scope of your assignment? I'd also assume that there's already a hashmap implementation in C++ that you can use.

A pointer is just a way to point to a location in memory. Say you have a queue of cars:

[1] [2] [3] [4] [5] [6] [7] [8]

and you want to always know where the front and the back are, you might have a pointer pointing to both like so:

[1] [2] [3] [4] [5] [6] [7] [8]
  ^..........................^

These are now two memory locations that you know point to the front and back of your queue. If you wanted to say, remove car number 8, you could simply move the "end" pointer to car number 7

[1] [2] [3] [4] [5] [6] [7] [8]
  ^......................^

Car number 8 is technically still there, but you no longer have any way to access it. It's totally lost, and as far as your program is concerned your queue now has 7 cars in it. You can also use pointers as a method of keeping objects linked together, for example:

[1] -> [2] ->  [3]  -> [4] ->  [5]  -> [6] ->  [7]  -> [8]
If you then wanted to find car 5, the logic would be something like:

for each car starting at the first one
{
   	is this the car I want? If so, complete. Otherwise, take me to the car that he points to.
}

Car 1 stores the memory location of car 2. Car 2 stores the memory location of car 3 etc and you keep going along the chain until you find the one you want. You generally store 3 pointers, the start, the end, and one you can move around.

#5 Lovato

Lovato
  • Banned from trading - Do not trade with this member!

  • 308 posts


Users Awards

Posted 24 October 2011 - 12:55 PM

You're in luck. I'm actually learning all this C++ stuff in my sophomore year at college, and it's somewhat of a difference from Java.

Now, Sida pretty much explained constructors, destructors and copy-constructors, etc....just ONE note on them: in a constructor, it's good practice to pass a reference to an object instead of the object itself. You want to minimize as many operations as possible. C++ is a very efficient language, but it relies heavily on the programmer. Now, you may say: goddamn it, but I don't get pointers and references and stuff.

Well, I'm going to be brutally honest with you - you HAVE to understand pointers to code efficiently, not only in C++, but in C or any pointed based language. Java is easy, you have the virtual machine do everything and we don't use pointers there. In C++, MEMORY allocation is super important. That's why we use pointers.

For example, it's much more cost-effective (we say it like this in programming "syntax", it means less operations) to pass a reference to an object than copy it to memory and then assign it to ANOTHER variable in memory. It's a waste, you use too much temporary memory. You might say "oh but my programs are light". Yeah, but imagine one day, you're building a heavy or critical app and it's crucial to minimize the processing time. You will carry on bad habits, and it's never good.

A reference is like saying "that object is THERE" instead of grabbing it, copying it to temporary memory and making a copy. The reference goes DIRECTLY to the spot in memory where that object is located and copies it. This is very important in functions because you don't need to mess around with temporary objects.

Say, for example, you have a class Place. A place has a name, right? So, what you'd do is, for example:

class Place {
protected:
	string name; // place name
	
public:
	Place();
	Place(const string &p);
	Place(const Place &p);
	~Place();

};

Place::Place(){ 
	this->name = NULL;
}

Place::Place(const string &p){ // ATTRIBUTE CONSTRUCTOR
	this->name = p.name;
}

Place::Place(const Place &p){  // COPY CONSTRUCTOR!
	this->name = p.name;
}

You may have noticed a const keyword before the object type. That means the argument I pass to the constructor is read-only, I can't modify it. In this case, we're only going to copy the attributes of p, we're not going to ALTER p, so it makes no sense to make it modifiable. Again, optional, but these are good practices.

And remember pointers:

int x = 1;
int *xp;
xp = &x;  // this passes the REFERENCE of x.
cout << xp; // this prints the memory address of x, imagine 0xFF00C0A (memory addresses are in hex value)
cout << *xp; // this prints out the CONTENT of x at memory position pointed by xp, which is 1.

-----------------

Now, dynamic memory. Imagine I have space for 10 cars in a parking lot. And I'm a happy camper because I'm the security guard (just for fun haha) and I say "no, no, only 10 cars enter at one time". And then my crappy boss decides to be a bitch and say "Now you have to let 20 cars in". So what do I do? In real life...well, I'd expand the parking lot with loads of money and stuff. In programming it's easier. Say you have a Car array and an integer that tells you how many cars are in the parking lot:

Car cars[10];
int numberofCars = 0;

So what do I have to do? I let 10 cars in and increment my numberofCars variable. Then when it's full (the var is 10), I create ANOTHER array of double the size (it's good practice to do this in C++ - 10, 20, 40, 80, and so on), COPY the Cars that are in the array to the new one, and then I still have 10 spaces left. Simple huh? If only life was this easy!

You could also use a linked list, stack, queue, basically any type of data structure in C++, but those are a little more complex because you work almost exclusively with pointers. With arrays it's easier, but you have to have a function that does the size verifying and stuff when you insert new things there.

As for operator overloading...well, that's basically redefining operators (like =, +, -, ++, --, << (stream out)) to doing what YOU want them to do for your objects.

Let's take the attribute operator (which is =).

Now, in standard C++, you can do this natively (include <iostream> and 'using namespace std;' in your headers):

string c = "Hello World";

string IS a native data type of C++ (BUT not C), so you can attribute directly. Why? Because in the <iostream> library there is an '=' overload that lets you do that.

However, when YOU create your objects you have to do this manually because the compiler can't guess. Sida pretty much explained that part. :)

That should cover most of it. If you need any future help, post here or PM me, I'll be happy to help!

Edited by Lovato, 24 October 2011 - 01:05 PM.


#6 Sida

Sida
  • Tsvetesman

  • 3865 posts

Posted 24 October 2011 - 01:38 PM

Lovato...you reminded me why I hate C++ xD I hate all that optimization...would much rather miss it all out and take 1/10000th of a millisecond longer :p I think I'd actually turn down a well paid job on a real-time system...bah. Hate it hate it hate it.

#7 Melchoire

Melchoire
  • 5284 posts


Users Awards

Posted 24 October 2011 - 04:11 PM

Looks like Sida and Lovato did a nice job giving you a run down, but maybe I can take a whack at it?

I completely agree with lovato in that you MUST learn pointers in order to be an effective programmer; one that knows all the ins and outs of exactly what their program is doing. This means you'll be even better at debugging them because you'll understand the different error messages and such.

I think I can cover the following questions:

1) How do I dynamically allocate memory?
2) How do I know if I don't allocate enough?
3) What does it look like in C++?

1) Dynamically allocating memory means at run-time at some point in your program you need to reserve some memory for whatever reason. When this happens the program will first need to know how much memory you need, once it knows this it allocates the necessary amount on the program heap(not the data structure).

2) Unfortunately you have to insure that the necessary amount is allocated in your program's logic. If you end up not allocating enough and make a reference to a piece of memory that's not allocated to you, you get a segmentation fault. There are some other reasons as well but generally a segfault means you're trying to access some memory that you're not allowed to...

3) In C++ you have access to 2 different functions that let you allocate memory. The easy one is the 'new' operator; it just returns a reference to the newly allocated block. You also have access to malloc, which is really meant to be used in C but you have it there, should you ever need. The opposite of 'new' is delete and the opposite of malloc is free

Here's how to use it: (I'm not actually compiling it but I'm sure it's correct)

//Allocate 19 integer-sized blocks for an int pointer
int* ptr = new int[19];
//So this means everything from ptr[0] to ptr[18] are valid and you can you read and write to them, no problem. But if you make a reference to
//ptr[19] you will get a segmentation fault

//Now lets free it:
delete [] ptr;
//Now any reference you make to ptr, will result in a segfault. 

//You don't have to allocate more than 1 block:
int* ptr = new int;
delete ptr;
//That also works...

//If you ever have to do the equivelant in C:
int* ptr = (int*)malloc(19 * sizeof(int));
//In this case malloc needs the exact number of bytes to allocate, hence why we use sizeof. It also returns a void pointer(void*) so you need
//to typecast it to a int* in the assignment

//To free it:
free(ptr);
//That simple...

I find this guy's explanations to be very helpful. He has some chapters on exactly what you're concerned with:
http://c.learncodethehardway.org/book/

Hope that helped...

Also I should note that the C and C++ standard have no methods for you to query how much memory is allocated to a pointer. So unless you keep track of that 19 up there, there's no way for you retrieve it. This is not the case for arrays, though.

int* ptr = new int[19];
//sizeof(ptr) = 8 (4 on a 32 bit machine) again these are in bytes...
int ptr[19];
//sizeof(ptr) = 76 (4 * 19)

So take that into consideration, it can be very problematic sometimes.

Some compilers have vendor specific functions that you let you get around this but they're not part of the standard.

Edit: as it turns out the examples I give do not create a segfault. Maybe it's just my compiler. Sorry about the confusion but the rest of what I said still remains valid.

#8 Guest_jcrgirl_*

Guest_jcrgirl_*

Posted 09 November 2011 - 10:02 AM

How do I implement an assignment operator, copy constructor, or copy things in general?

Like, right now I'm doing a binary search tree, and I have to copy a subtree, and make an assignment operator. The copy constructor just pulls what I do to the assignment operator so idk where to start :p

#9 Waser Lave

Waser Lave

  • 25516 posts


Users Awards

Posted 09 November 2011 - 10:20 AM

How do I implement an assignment operator


Erm, isn't = the assignment operator? O_o As in var blah = 1...

#10 Melchoire

Melchoire
  • 5284 posts


Users Awards

Posted 09 November 2011 - 11:47 AM

How do I implement an assignment operator, copy constructor, or copy things in general?

Like, right now I'm doing a binary search tree, and I have to copy a subtree, and make an assignment operator. The copy constructor just pulls what I do to the assignment operator so idk where to start :p


In your class definition you declare a function like so:

binary_tree operator=(binary_tree copy_from);

And you define it like:

binary_tree operator=(binary_tree copy_from)
{
//do some operations
return new_binary_tree;
}


Are you using templates? I have a bst I wrote my data structs class you can take a look at if you want...

#11 Guest_jcrgirl_*

Guest_jcrgirl_*

Posted 09 November 2011 - 05:33 PM

Erm, isn't = the assignment operator? O_o As in var blah = 1...


Well it is, but sometimes you need to use the operation +=
in which case you need to make a copy and store it, then add it to the next incremented value, copy and store it, add it to thenext incremented value and such :p
It wont work unless you specifically implement operator=()


In your class definition you declare a function like so:

binary_tree operator=(binary_tree copy_from);

And you define it like:

binary_tree operator=(binary_tree copy_from)
{
//do some operations
return new_binary_tree;
}


Are you using templates? I have a bst I wrote my data structs class you can take a look at if you want...



Yah I know the prototype. I just don't know how to actually make the copy of the tree.
I never really understood how to implement the function of copying step-by-step with dynamic memory

If I could guess it would be something like,
Oh
Create a "new" array of certain amount
Copy the "old" one into the "new" one
and then "delete" the old one (locally)

But I just dont understand how through syntax and through the actual copy process?
Uhh
Heres a snippit of code so I can explain better:


Struct TreeNode
// blablbalablabla the usual 

///////////////////////constructor/////////////////////////

		//default constructor for tree node: no memory allocation.

		//pointers default to null as a convenience measure.

		treeNode(val_type d=0, treeNode* l=0, treeNode* r=0);




		//////////////////data members://///////////////////

		val_type data; //the data of the node

		treeNode* left; //pointer to the left subtree

		treeNode* right; //pointer to the right subtree







/////////////////////copy subtree function//////////////////////
	treeNode* copyST(treeNode* original)

	{

		//PROBLEM FOR THE STUDENT

		return 0;

	}

/////////////////////////constructors//////////////////////////

	Tree::Tree()

	{

		root = 0;

	}




	Tree::Tree(const Tree& T)

	{

		this->root = copyST(T.root);

	}




	Tree::Tree(val_type* A, unsigned long size)

	{

		this->root = 0;

		//just sequentially insert items...

		for(unsigned long i=0; i<size; i++)

			this->insert(A[i]);

	}




	treeNode::treeNode(val_type d, treeNode* l, treeNode* r)

	{

		data = d;

		left = l;

		right = r;

	}




/////////////////////////assignment//////////////////////////

	void Tree::operator=(const Tree& T)

	{

		//PROBLEM FOR THE STUDENT

	}









That thing looks huge but just skip to the parts with "problem for student"
thats what I actually have to do and if you look at the copy constructor, it pulls what I implement as the assignment=()
I dont know how to implement that at all

and I dont think our prof likes templates



Edited by jcrgirl, 09 November 2011 - 05:35 PM.


#12 Melchoire

Melchoire
  • 5284 posts


Users Awards

Posted 09 November 2011 - 10:31 PM

Well it is, but sometimes you need to use the operation +=
in which case you need to make a copy and store it, then add it to the next incremented value, copy and store it, add it to thenext incremented value and such :p
It wont work unless you specifically implement operator=()





Yah I know the prototype. I just don't know how to actually make the copy of the tree.
I never really understood how to implement the function of copying step-by-step with dynamic memory

If I could guess it would be something like,
Oh
Create a "new" array of certain amount
Copy the "old" one into the "new" one
and then "delete" the old one (locally)

But I just dont understand how through syntax and through the actual copy process?
Uhh
Heres a snippit of code so I can explain better:

Spoiler

That thing looks huge but just skip to the parts with "problem for student"
thats what I actually have to do and if you look at the copy constructor, it pulls what I implement as the assignment=()
I dont know how to implement that at all

and I dont think our prof likes templates




You would instantiate a new binary tree. Then you perform a pre-order traversal, must be pre-order unless it's a self-balancing tree. At each step of the traversal you call your insert function like: "copy_of_tree->insert(current_node);" That should result in an identical binary tree.

Also it's more efficient to return a pointer to the new copy, otherwise when copying a big tree you're essentially creating 2 trees. One in the constructor and one in the routine that's calling it.

E: come to think of it, using a pointer would mean that you're not creating another copy of the tree at each recursive call! So yeh use a tree pointer and keep passing it in the traversal calls.

#13 Guest_jcrgirl_*

Guest_jcrgirl_*

Posted 10 November 2011 - 04:26 AM

The program creates a self balancing tree with recursion. The 3 traversals of the tree are 3 functions we have to implement :p
I already did those it was the easiest thing to do, lol

So would I still go about it the same way? Posted Image

Edited by jcrgirl, 10 November 2011 - 04:26 AM.


#14 Melchoire

Melchoire
  • 5284 posts


Users Awards

Posted 10 November 2011 - 07:11 AM

The program creates a self balancing tree with recursion. The 3 traversals of the tree are 3 functions we have to implement :p
I already did those it was the easiest thing to do, lol

So would I still go about it the same way? Posted Image


Pretty much. Pseudo code would look like:
copy(node, *tree_so_far)
{
	tree_so_far.insert(node);
	copy(node.left, &tree_so_far);
	copy(node.right, &tree_so_far);
}

The constructor would kick off this recursive function.

#15 Guest_jcrgirl_*

Guest_jcrgirl_*

Posted 11 November 2011 - 10:55 PM

Pretty much. Pseudo code would look like:

copy(node, *tree_so_far)
{
	tree_so_far.insert(node);
	copy(node.left, &tree_so_far);
	copy(node.right, &tree_so_far);
}

The constructor would kick off this recursive function.




I got it working :)
The last step is to remove actual nodes from the BST, and then I'm finally done and can submit my assignment, whoo!

It's pretty tricky but I don't think it's going to drive me as nuts as when I was tired and trying to write the size function. Forgot to add the last node at the top after adding left and right side of the tree. Omg 2 hours trying to figure out wtf was wrong. Ugh never again will I forget the actual root.

I'll post if I have trouble with remove()
Thank you :)

Edited by jcrgirl, 11 November 2011 - 10:56 PM.


#16 Melchoire

Melchoire
  • 5284 posts


Users Awards

Posted 11 November 2011 - 11:45 PM

I got it working :)
The last step is to remove actual nodes from the BST, and then I'm finally done and can submit my assignment, whoo!

It's pretty tricky but I don't think it's going to drive me as nuts as when I was tired and trying to write the size function. Forgot to add the last node at the top after adding left and right side of the tree. Omg 2 hours trying to figure out wtf was wrong. Ugh never again will I forget the actual root.

I'll post if I have trouble with remove()
Thank you :)


Glad to hear. =]


1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users