Hi,
just a little while ago I learned that it is possible to include the initialization for each element of a struct inside its definition. I think this is a great idea and I am doing it as follows (just to have it all here)
The above works fine for me but is there a way to do the same with a vector even though I might not know the number of elements in the beginning? Or is that an irrelevant question because it is not possible to append an element to a vector without defining its value (as push_back() will give an error)?
Hm, ok. I guess the last question was composed a little to quickly. I'll try again, this time a little more elaborate. Sorry!
So if Data d; calls the constructor it is clear that d.a and d.b are both set to -FLT_MAX but as std::vector<Data> v; will initialize a vector of size 0 there are no entries for v[i].a and v[i].b at all and I would get a out of range error as soon as I try to access any of them. So in this case the constructor would not be called (or ignored)? But I guess as long as I only use v.push_back(d) to grow my vector I should be save to always have a known state in the vector. In other words there is no need for a constructor for a vector.
The reason why I am concerned about this is that I need to expend the struct above to:
All of the data stored is e.g. temperatures but the problem is that I may have a different number of layerNode's each time I run the program.
Would the above then be sufficient to guarantee that I don't end up with random entries in any instance of vector<Data> x provided I only use x.layerNode.push_back(d) to grow my vector?
The default constructor is used by the vector if you use the vector's constructor that takes the number of elements you want in it by default:
1 2 3 4
Date d;
d.a = 1.0;
vector<Date> dvec (3); //contains three default-constructed Dates
dvec.pus_back(d); //dvec now contains an additional Date with Date::a being 1.0
Try this out:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
struct Test
{
Test(){cout<<"Default Constructor"<<endl;}
Test(const Test &from){cout<<"Copy Constructor"<<endl;}
Test &operator=(const Test &from){cout<<"Assignment Operator"<<endl;return*this;}
~Test(){cout<<"Destructor"<<endl;}
};
//...
Test t;
{
Test t2 (t);
t = t2;
}
vector<Test> tvec (3);
tvec.push_back(t);
Watch the output and see if you can figure out which lines do what ;)
#include<iostream>
#include<vector>
usingnamespace std;
struct Test
{
Test(){cout<<"Default Constructor"<<endl;}
Test(const Test &from){cout<<"Copy Constructor"<<endl;}
Test &operator=(const Test &from){cout<<"Assignment Operator"<<endl;return*this;}
~Test(){cout<<"Destructor"<<endl;}
};
int print(int i){
cout << endl << i << ' ';
return i+1;
}
int main(){
int i=1;
i=print(i); //1 value of i printed
Test t;
{
i=print(i); //2
Test t2 (t);
i=print(i); //3
t = t2;
}
i=print(i); //4
vector<Test> tvec (3);
i=print(i); //5
tvec.push_back(t);
i=print(i); //6
return 0;
}
The pint function is just to make it easier to see where in the code the output is generated. I am sadly out of time just now to completely understand what is doing what and why but I will spend some time on it tonight. But there are a bunch of new questions this has already brought up for me.
1) I know the code is just an example and therefor not intended to have a real function. However I find it sort of irritating that it is possible to build a struct such as Test. Is there any reason why it is possible? Essentially the struct does nothing. Shouldn't a struct always contain at least some data (or at least the possibility to do so)?
2) Is there any need to make an Assignment Operator? So far I have never defined that myself but it has always worked. So I assume it is here more for teaching purposes - thanks for including it - but there is no need to usually including it in "real live" struct's unless I want to overload (I hope I have finally understood the meaning of this) the = operator for this struct.
3) I have, so far never defined a destructor. I always assumed that it is only needed if I wish to distruct an instance before the end of its scope. Is that correct? If so I would assume that it is in here, again for teaching purposes - thanks again - but that all instances would be distructed in any case with when the end of main() is reached.
1. You should set the memory you want to, in that struct, and use it however you want to, but you can just hold some functions inside.
Let's say you have a class called Math.
You prepare some functions (static ones) Add, Sub, Mul, Dev, Pow, Abs, Sin...
You will then be able to call Math::Add, Math::Sub...
2. No, he just explained some extra functionalities you may have liked.
3. A destructor gets called when you do not need anymore a class/struct.
4. Yes.
1. You can always make an empty class struct, or union.
2 & 3. If you use dynamic memory inside the struct/class, there are a few problems then with the default copy constructor, default operator=, and default destructor the compiler generates.
class DynamicHolder
{
int *p; //just as an example
int size;
public:
DynamicHolder()
{
p = newint[100];
size = 100;
}
DynamicHolder(int num)
{
p = newint[num];
size = num;
}
int &operator[](int pos)
{
if(pos < size) return p[pos];
elsereturn size; //just so the code compiles, not a good idea
}
int Size()const{ return size; }
};
1 2 3 4 5
//Problem 1: What does this do?
int main()
{
DynamicHolder dh; //constructor makes it hold 100 elements
} //then it goes out of scope...but delete[] was never used!
1 2 3 4 5
//fix: add this to DynamicHolder:
~DynamicHolder()
{
delete[] p;
}
1 2 3 4 5 6 7
//Problem 2: What do these do?
int main()
{
DynamicHolder dh1; //p = new int[100]
DynamicHolder dh2 (50); //p = new int[50]
dh1 = dh2; //dh1.p = dh2.p
} //dh2 uses delete[] on p, then dh1 uses delete[]...on the exact same memory! (==BAD!)
1 2 3 4 5 6 7 8
//Fix: define your own operator=:
DynamicHolder &operator=(const DynamicHolder &from)
{
delete[] p;
size = from.size;
p = newint[size];
for(int i = 0; i < size; ++i) p[i] = from.p[i];
}
1 2 3 4 5 6 7 8 9 10 11
//Problem 3: same as before but more subtle
void CrazyPrint(DynamicHolder dh) //no reference to prevent modifying original
{
for(int i = 0; i < dh.Size()-1; ++i) dh[i] = dh[i+1]*2;
for(int i = 0; i < dh.Size(); ++i) cout << dh[i]/3 << " ";
} //delete[] p
int main()
{
DynamicHolder mydh; //p = new int[100]
CrazyPrint(mydh); //dh.p = mydh.p
} //delete[] p...again! ahhh!
1 2 3 4 5 6 7
//fix:
DynamicHolder(const DynamicHolder &from)
{
p = newint[from.size];
size = from.size;
for(int i = 0; i < size; ++i) p[i] = from.p[i];
}
So, if you use dynamic memory, this automatically means you MUST (should) define the copy constructor, operator=, and destructor yourself. The compiler will never guess and do these things for you.
4. Never call the destructor yourself. You only do that if you use placement new, and you would use placement new in extremely rare cases. But, the syntax is that of MyObject.~MyClass(); just in case you were curious. Destructors are called automatically for you when you use delete or delete[] on pointers, or when normal variables just go out of scope (eg end of a function)
1) I know the code is just an example and therefor not intended to have a real function. However I find it sort of irritating that it is possible to build a struct such as Test. Is there any reason why it is possible? Essentially the struct does nothing. Shouldn't a struct always contain at least some data (or at least the possibility to do so)?
Obviously the utility of such a construct is limited, but it does see use. Classes intended to be abstract base classes, for instance.
2) Is there any need to make an Assignment Operator? So far I have never defined that myself but it has always worked. So I assume it is here more for teaching purposes - thanks for including it - but there is no need to usually including it in "real live" struct's unless I want to overload (I hope I have finally understood the meaning of this) the = operator for this struct.
Sometimes there is, sometimes there isn't. By default one is generated for you. You only need to supply one if the default behavior of member-wise assignment isn't appropriate for an instance of your class. The most common example of this is when one of your members is a pointer that points to memory that was dynamically allocated via new. If you don't supply an operator=, which of the two objects now containing a copy of that pointer are responsible for freeing that memory?
3) I have, so far never defined a destructor. I always assumed that it is only needed if I wish to distruct an instance before the end of its scope. Is that correct? If so I would assume that it is in here, again for teaching purposes - thanks again - but that all instances would be distructed in any case with when the end of main() is reached.
You need a destructor any time there is something you need done when an instance of a class is destroyed, whether it's freeing a resource (closing a file, returning memory, releasing a lock) or passing a message to another object.
Never destruct an instance before the end of it's scope. That means you've got an accessible object hanging around with an indeterminate state, and accessing that object will result in undefined behavior.