two types of heap allocated objects--any benefit?

N

newbie

Let's see two different usage of an STL container. I see (2) more
often when reading code over (1), dose that give any benefit or it's
purely a coding preference?

Also, please see the (3), I see (3) often in ppl' code, does that give
any benefit over (2)

Thanks for answering in advance:)

-------------------------------
class MyData;
class Example;

int main() {
Example example;
for(int i = 0 ; i < HUGE_NUM; i++) {
example.Enque(MyData(i));
}
}

class MyData {
public:
MyData(int i) {data = i; }
int data;
}

// (1) The first Example definition:
class Example {
public:
Example() {};
~Example() {};
Enque(MyData a) { storage.push_back(a); }
private:
deque<MyData> storage;
}


// (2) We can define Example alternatively, like the following
class Example {
public:
Example() { storage = new deque<MyData>; }
~Example() { delete storage; };
Enque(int a) { storage->push_back(a); }
private:
deque<MyData> *storage;
}

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

(3) The third approach doing similar thing.
class MyData;
class Example;
int main() {
Example example;
for(int i = 0 ; i < HUGE_NUM; i++) {
MyData* a = new MyData(i);
example.Enque(a);
}
}

class MyData {
public:
MyData(int i) {data = i; }
int data;
}

class Example {
public:
Example() { storage = new deque<MyData>; }
~Example() {
while (!storage->empty()) {
MyData* a = storage->pop_front();
delete a;
}
delete storage;
}
Enque(MyData *a) { storage->push_back(a); }
private:
deque<MyData*> *storage;
}
 
V

Victor Bazarov

newbie said:
Let's see two different usage of an STL container. I see (2) more
often when reading code over (1), dose that give any benefit or it's
purely a coding preference?

Also, please see the (3), I see (3) often in ppl' code, does that give
any benefit over (2)

Thanks for answering in advance:)

OK, let's rewrite this a tiny bit...

class MyData { ... }; // common to every case

(1)
class Example {
std::deque<MyData> storage; // an object
public:
void Enque(MyData const& d)
{ storage.push_back(d); } // storing a copy
};

(2)
class Example {
std::deque<MyData> *storage; // a pointer
public:
void Enque(MyData const& d)
{ storage->push_back(d); } // storing a copy
};

(3)
class Example {
std::deque<MyData*> *storage; // a pointer
public:
void Enque(MyData* pd)
{ storage->push_back(pd); } // storing a pointer
};

Every case has its use. (1) is common and easy to understand
and maintain. Extra copies are made of 'MyData', and it's not
use polymorphically. (2) Is not really different from (1),
except that the member 'storage' is dynamically allocated.
Makes no difference, really. Is harder to maintain than (1).
(3) Stores pointers to MyData. That's a totally different
container since it allows polymorphic use of the objects stored
in 'storage'. The fact that 'storage' is a pointer makes no
difference (and no sense either, like in case 2). If you intend
to store and to use 'MyData' polymorphically, it's better to
have a combination of (1) and (3):

class Example {
std::deque<MyData*> storage; // an object
public:
void Enque(MyData* pd)
{ storage.push_back(pd); } // storing a pointer
};

V
 
N

newbie

OK, let's rewrite this a tiny bit...

class MyData { ... }; // common to every case

(1)
class Example {
std::deque<MyData> storage; // an object
public:
void Enque(MyData const& d)
{ storage.push_back(d); } // storing a copy

};

(2)
class Example {
std::deque<MyData> *storage; // a pointer
public:
void Enque(MyData const& d)
{ storage->push_back(d); } // storing a copy

};

(3)
class Example {
std::deque<MyData*> *storage; // a pointer
public:
void Enque(MyData* pd)
{ storage->push_back(pd); } // storing a pointer

};

Every case has its use. (1) is common and easy to understand
and maintain. Extra copies are made of 'MyData', and it's not
use polymorphically. (2) Is not really different from (1),
except that the member 'storage' is dynamically allocated.
Makes no difference, really. Is harder to maintain than (1).
(3) Stores pointers to MyData. That's a totally different
container since it allows polymorphic use of the objects stored
in 'storage'. The fact that 'storage' is a pointer makes no
difference (and no sense either, like in case 2). If you intend
to store and to use 'MyData' polymorphically, it's better to
have a combination of (1) and (3):

class Example {
std::deque<MyData*> storage; // an object
public:
void Enque(MyData* pd)
{ storage.push_back(pd); } // storing a pointer

};

V

Thanks a lot. Very helpful.
 
P

pmouse

Thanks a lot. Very helpful.

There is a case where you would want to keep your member data as
pointers, that's when you don't want to call the default constructor
on them, hence you create them manually.

For example, the stl containers takes iterator inputs as constructor
parameter, if you want to call those constructors, keep them as
pointers.

regards,

PQ
 
?

=?iso-8859-1?q?Erik_Wikstr=F6m?=

There is a case where you would want to keep your member data as
pointers, that's when you don't want to call the default constructor
on them, hence you create them manually.

For example, the stl containers takes iterator inputs as constructor
parameter, if you want to call those constructors, keep them as
pointers.

Another case when you want to store pointers to the data instead of
copies are when you want to have to same object in multiple containers
(or rather pointers to the same data). Something like:

Example ex1;
Example ex2;
for (int i = 0; i < HUGE; ++i) {
MyData* a = new MyData();
ex1.Enque(a);
ex2.Enque(a);
}

Than any changes made to objects in ex1 will also be made to those in
ex2, which would not be the case if you used methods 1 or 2.
 
J

James Kanze

newbie said:
Let's see two different usage of an STL container. I see (2) more
often when reading code over (1), dose that give any benefit or it's
purely a coding preference?
Also, please see the (3), I see (3) often in ppl' code, does that give
any benefit over (2)
Thanks for answering in advance:)

[...]
// (1) The first Example definition:
class Example {
public:
Example() {};
~Example() {};
Enque(MyData a) { storage.push_back(a); }
private:
deque<MyData> storage;
}

This is more or less that standard way of doing things in C++.
// (2) We can define Example alternatively, like the following
class Example {
public:
Example() { storage = new deque<MyData>; }
~Example() { delete storage; };
Enque(int a) { storage->push_back(a); }
private:
deque<MyData> *storage;
}

And I've never seen this. What's the point? (In my experience,
pointers to standard containers are very, very rare. The only
occur when several objects share the same container.)
(3) The third approach doing similar thing.
class MyData;
class Example;
int main() {
Example example;
for(int i = 0 ; i < HUGE_NUM; i++) {
MyData* a = new MyData(i);
example.Enque(a);
}
}
class MyData {
public:
MyData(int i) {data = i; }
int data;
}
class Example {
public:
Example() { storage = new deque<MyData>; }
~Example() {
while (!storage->empty()) {
MyData* a = storage->pop_front();
delete a;
}
delete storage;
}
Enque(MyData *a) { storage->push_back(a); }
private:
deque<MyData*> *storage;
}

Again, fairly rare. As I mentioned above, it would be very rare
to have a pointer to a container. It's not particularly rare,
however, for containers to contain pointers, and while not
usually the case, it sometimes happens that they own the
pointers as well, and are responsible for deleting them. (This
most often happens when the contained object has identity, and
is not copiable.)
 
G

Gavin Deane

There is a case where you would want to keep your member data as
pointers, that's when you don't want to call the default constructor
on them, hence you create them manually.

For example, the stl containers takes iterator inputs as constructor
parameter, if you want to call those constructors, keep them as
pointers.

Are you suggesting a case where the OP's example 2 might be useful?
Because if so, I don't see what your getting at. I can have a
std::deque member and initialise it in any way I want.

#include <deque>
using namespace std;

class Example
{
public:
Example(deque<int>::iterator begin,
deque<int>::iterator end)
: storage(begin, end) {}
private:
deque<int> storage;
};

int main()
{
deque<int> d;
d.push_back(42);
Example e(d.begin(), d.end());
}

Gavin Deane
 
F

Fei Liu

Another case when you want to store pointers to the data instead of
copies are when you want to have to same object in multiple containers
(or rather pointers to the same data). Something like:

Example ex1;
Example ex2;
for (int i = 0; i < HUGE; ++i) {
MyData* a = new MyData();
ex1.Enque(a);
ex2.Enque(a);
}

Than any changes made to objects in ex1 will also be made to those in
ex2, which would not be the case if you used methods 1 or 2.
This often hints at a logical flaw in software design. I would verify
carefully that this is something warranted. This causes a lot of trouble
in multi-threaded programs.
 
F

Fei Liu

Gavin said:
Are you suggesting a case where the OP's example 2 might be useful?
Because if so, I don't see what your getting at. I can have a
std::deque member and initialise it in any way I want.

#include <deque>
using namespace std;

class Example
{
public:
Example(deque<int>::iterator begin,
deque<int>::iterator end)
: storage(begin, end) {}
private:
deque<int> storage;
};

int main()
{
deque<int> d;
d.push_back(42);
Example e(d.begin(), d.end());
}

Gavin Deane

He's talking about case (3).
 
G

Gavin Deane

He's talking about case (3).

You sure? The OP's case 3 had a member pointer-to-deque, just like
case 2, but the deque stored pointers rather than objects. I can see
why sometimes you might want a container of objects and other times a
container of pointers. But I was responding to a comment about "member
data as pointers" (so far in this thread, the member data is the
container itself) and not wanting to use the default constructor for
the container. Whether the container happens to store pointers or
objects doesn't seem to me to be relevant to the comment from pmouse
that I quoted. And if the difference between the container holding
pointers or objects is irrelevant, the OP's cases 2 and 3 are
identical - in both cases class Example holds a pointer-to-deque and
my comments apply equally to cases 2 and 3.

pmouse's comment looked to me like a reason for preferring the type of
the member in class Example to be pointer-to-deque rather than deque.
It didn't look to me like pmouse was suggesting a reason for
preferring to store pointers rather than objects in the deque.

I can't immediately see any reason for class Example to hold a pointer-
to-deque rather than a deque, whether the container holds objects or
pointers.

Gavin Deane
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top