I have a struct like this:
struct MYSTRUCT {
....
};
if I create struct objects with a for loop like this:
for(int i = 0; i < 2; i++){
MYSTRUCT *mystruct;
}
will mystruct be the same every time because of the same name? What's the difference between the code above and this code:
MYSTRUCT *mystruct0;
MYSTRUCT *mystruct1;
How can I create different struct objects in a simpler way?
MYSTRUCT *mystruct0;
MYSTRUCT *mystruct1;
...
MYSTRUCT *mystruct99;
MYSTRUCT *mystruct100;
If I do the way below, will all the struct pointers same or they are isolated from each other?
vector<MYSTRUCT *> mystructs;
for(int i = 0; i < 100; i++){
MYSTRUCT *mystruct;
mystructs.push_back();
}
First off, you should never try to generate variable names.
it's just not the way to do it.
You should use a container (like in your example a std::vector) which is exactly what you want : something to put several other things of the same type.
In your example :
struct MyStruct { // Don't name your struct in all caps.
// it's generally reserved for MACROS. Use CamelCase
// blablabla
}
Do not use pointers unless you need to, usually it is much easier and much more efficient to use values. If you need to pass one of the elements of your vector to a function, so this function can modify it, you can pass an iterator to that element.
vector<MyStruct> mystructs;
for(int i = 0; i < 100; i++){
mystructs.emplace_back(MyStruct{});
}
Also if your struct has fields that need to be initialized, you might want to use an initializer list or define a constructor.
If you need to use dynamic allocation you can do something like this :
vector<MyStruct*> mystructs;
for(int i = 0; i < 100; i++){
mystructs.emplace_back(new MyStruct{});
}
Now the vector contains the pointers, not the objects. Each object is allocated individually in his own memory emplacement, therefore there is no garantee that they are next to each other.
This matters tremendously for performance : Pointers give you indirection.
Indirection leads to pain. Pain leads to anger ... blablabla => Welcome to the dark side.
A struct is a datatype that you defined, when you do MYSTRUCT *mystruct you declare a pointer to a MYSTRUCT object and the pointer is called mystruct It is the same thing you do with int *pointer_to_int.
For you first version, you redeclare mystruct at each iteration while your second declares two independant variables. If you want to declare a set of MYSTRUCT* easily, use an array of MYSTRUCT*: MYSTRUCT* mystruct_pointers_array[100].
NOTE MYSTRUCT* pointers are different than MYSTRUCT variables, you need to dereference the pointer to access to MYSTRUCT elements. E.g. mystruct->x.
In your example code:
for(int i = 0; i < 2; i++){
MYSTRUCT *mystruct;
}
You're not creating objects at all. All you're doing is declaring pointers to MYSTRUCT. These pointers are uninitialized and therefore don't point to anything (yet). Moreover, the pointer (mystruct) itself is allocated on the stack and has a lifetime limited to the scope of your for-loop.
I am not sure what you are trying to achieve, but if you need multiple objects of type MYSTRUCT, you should not try to create them in a loop like that. Rather, declare an array or std::vector and initialize appropriately. I will leave it to you to figure out how to do this. When you do, be aware of the difference between static and dynamic arrays, and their consequences for memory management!
EDIT:
On request, I'll elaborate on the final snippet you provided:
vector<MYSTRUCT *> mystructs;
for(int i = 0; i < 100; i++){
MYSTRUCT *mystruct;
mystructs.push_back();
}
What happens here is you declare an empty vector of pointers to MYSTRUCT. So far so good. Now, judging on the body, you want to populate this vector with pointers to actual objects, but this is not happening in your code. I doubt this will even compile, given that push_back requires a MYSTRUCT* as an argument. Therefore, to do what you intend to do, your code should in each iteration:
Allocate an object on the heap.
Push a pointer to this object to the back of the vector.
This would look like the following:
vector<MYSTRUCT*> vec;
for (int i = 0; i != n; ++i) // pre-incr is good practice!
vec.push_back(new MYSTRUCT); // optionally add constructor arguments
However, this introduces you to new responsibilities: you are the one requesting the memory explicitly using new, so you should free the memory explicitly using delete. Unless MYSTRUCT is polymorphic (contains virtual members), you can easily circumvent this (even it it is polymorphic, you shouldn't do this, but that's another topic).
The answer to this problem is: don't store pointers, store objects:
vector<MYSTRUCT> vec; // no pointer!
for (int i = 0; i < 100; ++i)
vec.push_back(MYSTRUCT()); // create an unnamed instance and copy to the back of the vec
There, you have just created a vector of n MYSTRUCT instances. No pointers, no new and delete, simple and easy!
Use a std::vector for this purpose:
std::vector<MYSTRUCT *> v(100);
for (auto& pointer : v)
pointer = new MYSTRUCT(/*...*/); //initializating (writing address to pointer)
In your way you just initializing a object of pointer type. In other words you allocating memory only for pointer, where you must write address to your structure or nullptr-value.
Others have said similar things to this, but specifically I'd do it like this:
std::vector <MYSTRUCT *> vect;
for(int i = 0; i < 2; i++){
vect.push_back();
}
Then you can just access each element like an array, like vect[0]. Plus with std::vector you can access the first element (element 0) with vect.front() and the last with vect.back().
Though it's useless without assigning a MYSTRUCT that has been initialized separately, I can see the usefulness of struct * vectors.
Related
I have a shared array, it is something like that
class Array
{
public:
// basic array opertion like resize, operator[], etc, like std::vector
private:
int size;
std::shared_ptr<int> data;
};
It do the shallow copy in copy constrcutor and copy assigen operator.
The memory of Array is controlled by std::shared_ptr to ensure no memory leak can happen.
Now I have a class, ArrayContainer , it have Array as the member.
I need let others access the class member. but I do not want others change it. My design is follow, but it is not good.
class ArrayContainer
{
public:
void calculation()
{
// do some operation on array ...
}
// it is not a safe interface, althogh const, others still can change the member
const Array &getArray() const
{
return this->array;
}
private:
Array array;
};
Using my interface, one may access the member in ArrayContainer by follow ways.
// example 1: safe use array
ArrayContainer container;
const Array &array = container.getArray();
// follow using array will not influence the member in user.
// example 2: may be not safy use array
ArrayContainer container;
Array array = container.getArray();
// follow using array may modify the array in container, it is not safe.
My problem is: I do not want to other's may change the member in ArrayContainer. What is a elegant way to access a shared array in class with safety?
Thanks for your time.
I don't understand why you are using std::shared_ptr, since you want memory leak to not happen.
I suppose you are using std::shared_ptr<int[]> data, and not std::shared_ptr<int> data;
I guess enough for you to use std::unique_ptr, and don't share your memory.
Anyway you need to add copy constructor to full copy array data, because in this case
ArrayContainer container;
Array array = container.getArray();
will be called copy constructor
You can do it like below
Array(const Array& array) {
for (int i = 0; i < array.size; ++i) {
data.get()[i] = array.data.get()[i];
}
}
I have a class A like:
class A
{
int a;
}
And, Also I have class B that inherited class A:
class B : public A
{
int b;
public:
static A** ReturnAPtrArray(int size);
}
Then, I make Array having A class Pointer in class B.
A** B::ReturnAPtrArray(int size)
{
A** array = new A*[size];
for(int i = 0; i< size; i++)
{
array[i] = new A();
}
return array;
}
In main func, I Called class B's ReturnAPtrArray() func.
void main(void)
{
int size = 100;
A** aptrArray = B::ReturnAPtrArray(size);
--------Do Something
delete[] aptrArray;
}
This main func makes memory leak. So I deleted every pointers like this:
void main(void)
{
int size = 100;
A** aptrArray = B::ReturnAPtrArray(size);
--------Do Something
for(int i = 0; i< size; i++)
{
delete aptrArray[i];
}
delete[] aptrArray;
}
After modified main func, memory leaks were disappeared.
If I want to free memory, should I delete all pointers in Pointer Array?
Is there any other options?
If I want to free memory, should I delete all pointers in Pointer Array?
Yes you should
delete[] deletes only the array it self.
Since you have a array of pointers you must delete every pointer element individually.
As for other options you can use smart pointers.
Example:
#include <memory>
#include <vector>
int main()
{
std::vector<std::shared_ptr<A>> array;
for(int i = 0; i < 100; i++)
{
array.push_back(std::make_shared<B>());
}
}
when the array goes out of scope it deletes it self
It is important to distinguish between polymorphic-ownership and polymorphic use. Polymorphic ownership is when you want to own a thing (or many things) that are are of an unknown type. Polymorphic use is when you want to manipulate a thing when you don't know what it is. Your example doesn't really make clear why you use inheritance at all, so I will explain both.
If you only create B's just declare them as B's. If you want to pass a set of B's to a function that doesn't know they are B's, the simply create a vector of B's and pass them as pointer-to-A.
Like this ...
std::vector<B> myBs(5); // create 5 default B's
for (const auto& thisB: myBs) Fn(&thisB); // cast a ptr-to-B to a ptr-to-A
Keeping it simple like this will make your life a lot simpler, as this code is type-safe.
If on the other hand you want to own a list of things that might be a B, or might not, but definitely inherits from A. Use something like this.
std::vector<std::unique_ptr<A>> my_maybe_bs_might_not;
This pattern might seem superficially simpler, but it comes with a lot of gotcha's. For example, you must use must use a virtual destuctor. This in turn invokes the rule-of-3/5. Simply put, if the compiler doesn't know a thing is, you have to tell it how it can be moved/copied. If you don't, the compiler will probably do the wrong thing.
A simpler (assuming you have C++17) scheme for polymorphic-ownership is using a variant. When you use a variant, you must list all things it might be, this can include smart pointers.
using MaybeB = std::variant<std::unique_ptr<B>, std::unique_ptr<C>>;
std::vector<MaybeB> my_maybe_bs_might_not;
This pattern allows the compiler to generate all the right code for you, while making it simple to add new classes. The only downside is that because you must list all the things you might want to own. This makes it a bad choice for a library-level system, where the user of the library might want to write their own class (derived from A) and add it to your list.
The general advice is to pick the simplest scheme possible, which in general means avoiding polymorphic ownership unless that is really required.
I'm currently refactoring and change existing code to C++11 and I wonder if have memory leak. My code has a struct with a std::vector in it as well as a method to shrink() this vector down to its negative elements.
struct mystruct_t {
int other_stuff;
std::vector <int> loc;
// Adds elements to loc vector
void add(int pos){
loc.push_back(pos);
}
// Shrink the list
void shrink () {
std::vector<int> tmp;
for (unsigned int i = 0; i < loc.size(); ++i) {
if (loc[i] < 0) tmp.push_back (loc[i]);
}
loc = tmp;
std::vector<int>(loc).swap (loc);
}
mystruct_t(): otherstuff(0) {};
};
In another function I create a new instance of this struct like this:
mystruct_t c = new mystruct_t;
c->add(2);
c->add(3);
...
And later I call the shrink() method of this struct.
c->shrink()
Now I'm not sure what's happening with the "old" loc vector after the shrink function?
Will it get destroyed automatically or do I have to destroyed by hand? And if the later, how would I do that?
I also tried to change shrink() to more C++11 style by change it to:
void shrink (){
std::vector<int> tmp;
for (auto &currLoc : loc) {
if (currLoc < 0) tmp.push_back (currLoc);
}
loc = std::move(tmp);
}
But the question remains the same what is happening to the "old" loc vector additionally this seems to increase the memory usage. I'm new to C++11 and not sure if I totally misunderstand the concept?
Now I'm not sure what's happening with the "old" loc vector after the shrink function?
There is no "old" loc vector. Through the lifetime of a mystruct_t object, it has exactly one member vector loc. You never get a new member or throw away an old one.
When you copy assign to the member (loc = tmp;), the buffer - cotained within the vector - is renewed. The vector owns the buffer, and the vector takes care that it is destroyed properly. Same applies when you move assign in the c++11 version.
Will it get destroyed automatically
If you refer to the memory allocated by the vector, then yes.
or do I have to destroyed by hand?
You have to destroy by hand only whatever you created by hand. You didn't call new, so you don't call delete.
additionally this seems to increase the memory usage.
Your c++11 version lacks the "shrink to fit" part of the original (std::vector<int>(loc).swap (loc);). In c++11 you can do:
loc = std::move(tmp);
loc.shrink_to_fit();
In the pre c++11 version, can get rid of the copy assignment and simply construct the temporary from tmp, and swap it with loc:
std::vector<int> tmp;
// copy the objects you want
std::vector<int>(tmp).swap(loc);
Operation std::move just casting values, so there is no additional memory usage.
When you use std::move compiler will remove head address of first object, and just reassign memory to second object. So it's very fast operation, etc just changing the head of data.
I would like to know, if I have a class with an array attribute whose size is not the same for all instances :
class myObject{
private:
int size;
int* array;
// other methods/attributes
};
Is it obligatory allocated using new ?
explicit myObject(int size = 0):size(size){
array = new int[size];
}
Even if in the main(), I always use constant parameters to create the instances of the class ? (Meaning I know every array size at compile time).
int main{
myObject object (5);
return 0;
}
Apparently something like :
private:
int size;
int array[size];
wont work, no ?
That means that array attribute whose size are not constant of the class are obligatory on the heap ?
Thank you for your answers,
That class contains no array. What you called array is a pointer; you cannot store any ints in it. If you really do just store a pointer, you'll have to allocate the memory yourself somehow; it can't magically appear. You'll also have to deallocate it yourself, and make sure that copying and assigning myObject objects doesn't cause any issues.
However, it's unlikely that a pointer is really the best way to do things. The standard library provides the std::vector class template which lets you use almost exactly the syntax you want:
class myObject {
std::vector<int> vector;
public:
myObject() {};
explicit myObject(std::size_t n) : vector(n) {}
};
With this in place you can create myObjects and they'll have the right amount of storage ready for them. It'll likely be dynamically allocated using operator new[], just like if you'd do it manually, but you don't have to worry about copying or deleting it.
int main() {
myObject a; // default-constructed, vector is empty.
myObject b(10); // std::size_t constructor, vector has 10 elements.
} // scope exit, b and a destroyed.
You can use the vector member much like if it was an array; the only thing it does not support is implicit decay to pointer, but the data member function makes up for even that.
As an alternative, if you always know the size at compile-time you can change the class into a class template and make the size a template parameter:
template<std::size_t N>
class myObject{
std::array<int, N> array;
// other methods/attributes
};
However, note that you now cannot use myObject<10> to a function expecting myObject<20>.
It is unlikely that you want more control than the above possibilities provide -- std::vector can be given an allocator, so it can do almost all work for you -- you could use std::unique_ptr<int[]> and make_unique together to make things work for you. However, if you need this kind of power, you probably know it yourself.
As a closing note, if you're just learning C++ and your book doesn't cover std::vectors somewhere early on, perhaps it's best to get a different book; they're one of the most commonly-useful data structures in the standard library and definitely not something to be left in an appendix.
If you need a variable sized array as a member of a class, don't use built-in arrays directly. Instead, use std::vector<T>, e.g.:
class myObject {
std::vector<int> array;
public:
explicit myObject(int size = 0): array(size){}
};
You can get the std:vector<int>'s size using array.size(), i.e., there is no need to store the size separately. Also, the content is automatically default initialized.
If I add elements to a vector using the code below, then at the time I call foo, the elements (automatic variables) of vec have been destroyed since the scope in which they are created ends.
std::vector<A> vec;
for (int i = 0; i < n; i++) {
A a;
vec.push_back(a);
}
foo(vec);
My question is now what the textbook solution to such a problem is
No, the elements in vec will be different copies of a.
However, you need to allocate the size of vec if you want to use operator[] or else use vec.push_back():
for (int i = 0; i < n; i++) vec.push_back(A());
EDIT (after question change):
Even though push_back() takes its argument as a reference, internally it will make a copy of it. It takes it argument by reference to avoid making an unnecessary copy prior to making the copy to store internally.
Don't worry about stack variables. When you push value in std::vector, this container creates heap copy of the variable. Thus, all your variables will exists, when you're live the scope.
you can define your variable as global and in your loop just let the value in that variable and then push back