Problem with initialization of array of class objects

Y

yatko

Hi all;

I want to define an array of objects and initialize them, but I don't
know how I could do that simply. I have searched over net, and have
found a few solutions. Does anybody has a better, simple solution?

Suppose there is a Foo class that has const member.

class Foo
{
public:
Foo(int, int);
~Foo();


private:
const int ID;
double int var;
};

First method:

Foo objects [MAX] = {Foo(0,1),Foo(1,2),Foo(2,3)};

// This one requires copy constructor, but I have const members, so it
doesn't work for me.

Second method:

std::vector<Foo> objects;

objects[0] = * (new Foo(0,1));
objects[1] = * (new Foo(1,2));
objects[2] = * (new Foo(2,3));

//This one solves problem of const members, but I dont want to use
vector.

I want to initialize the array as following, but it doesn't work.

Foo objects[MAX] = {{0,1},{1,2},{2,3}};


Thanks

yatko
 
A

Alf P. Steinbach

* yatko:
Hi all;

I want to define an array of objects and initialize them, but I don't
know how I could do that simply. I have searched over net, and have
found a few solutions. Does anybody has a better, simple solution?

Suppose there is a Foo class that has const member.

class Foo
{
public:
Foo(int, int);
~Foo();


private:
const int ID;

In general it's a good idea to reserve all uppercase names for macros.
Using them for constants is a Java'ism. Leads to problems in C++.

double int var;

There's no "double int" type in C++.

};

First method:

Foo objects [MAX] = {Foo(0,1),Foo(1,2),Foo(2,3)};

This method works nicely.


// This one requires copy constructor, but I have const members, so it
doesn't work for me.

It's opposite: the method below requires copy constructor, while the one
above doesn't.

Second method:

std::vector<Foo> objects;

objects[0] = * (new Foo(0,1));
objects[1] = * (new Foo(1,2));
objects[2] = * (new Foo(2,3));

//This one solves problem of const members, but I dont want to use
vector.

This doesn't work for const members.

I want to initialize the array as following, but it doesn't work.

Foo objects[MAX] = {{0,1},{1,2},{2,3}};

Works OK.

Post actual code that doesn't work (see the FAQ item how to get help
with Code That Does Not Work).


Cheers, & hth.,

- Alf
 
V

Victor Bazarov

yatko said:
I want to define an array of objects and initialize them, but I don't
know how I could do that simply. I have searched over net, and have
found a few solutions. Does anybody has a better, simple solution?

Suppose there is a Foo class that has const member.

class Foo
{
public:
Foo(int, int);
~Foo();


private:
const int ID;
double int var;

There is no such type as "double int".
};

First method:

Foo objects [MAX] = {Foo(0,1),Foo(1,2),Foo(2,3)};

// This one requires copy constructor, but I have const members, so it
doesn't work for me.

Why is that? Does the compiler complain? (beyond 'double int', I mean)
Second method:

std::vector<Foo> objects;

objects[0] = * (new Foo(0,1));
objects[1] = * (new Foo(1,2));
objects[2] = * (new Foo(2,3));

//This one solves problem of const members, but I dont want to use
vector.

No, it does not solve the "problem of const members". Putting your
objects in a vector requires the presence of the copy constructor
as well. Not to mention that you need to use 'push_back' instead of
assigning since elements objects do not exist unless you give the
vector some substance (and declaring it does not give substance, not
even empty space). Not to mention a memory leak (actually three
memory leaks).
I want to initialize the array as following, but it doesn't work.

Foo objects[MAX] = {{0,1},{1,2},{2,3}};

Correct. Brace-enclosed initialisers don't work for classes with
parameterized constructors like yours.

You might want to consider generating IDs from a static function;
that way you don't have to provide any and the constructor that takes
one argument could get/generate the ID automatically.

Also, please get into the habit of stating the *requirements*, not
showing non-working solutions, if you want help in class design.

V
 
D

dave_mikesell

In general it's a good idea to reserve all uppercase names for macros.
Using them for constants is a Java'ism. Leads to problems in C++.

Isn't it more of a C'ism? As in #define MAX ...?
 
A

Alf P. Steinbach

* (e-mail address removed):
Isn't it more of a C'ism? As in #define MAX ...?

No.

The Java convention (probably) stems from the C convention to use all
uppercase for macros. In early C one had to use macros for constants,
so constants also typically had all uppercase names. But it's been some
years since 1972, and C++ gives the ability to define constants without
using macros.

Since macros don't respect scopes it's generally not a good idea to let
non-macro names enter that "namespace" in C++ (note: Java doesn't have
this problem, because Java doesn't have a preprocessor).


Cheers, & hth.,

- Alf
 
Y

yatko

Hi guys;

Sorry for silly 'double int' type mistake, I know that there is no
such type, too. I just made mistake when writing the class definition
to the group. Anyway, now you can find actual codes. These are
Variable and ReaedersNWriters classes. As you can see, I have defined
a const member mode , takes values of BLOCKING, TIMED_WAIT,
NONBLOCKING , in ReadersNWriters class.

namespace Monitor{
template <class DataType>
class Variable : public ReadersNWriters
{
public:
Variable(WaitingMode);
~Variable();

bool Read(DataType&);
bool Write(const DataType&);

private:

DataType data;
boost::mutex enter;
};
}


class ReadersNWriters
{
protected:
typedef boost::mutex::scoped_lock scoped_lock;

ReadersNWriters(WaitingMode);
~ReadersNWriters();

bool BeginRead(scoped_lock&);
void EndRead(void);

bool BeginWrite(scoped_lock&);
void EndWrite(void);

private:
const WaitingMode mode;
int waitingReaders;
int nReaders;

int waitingWriters;
int nWriters;

boost::condition canRead;
boost::condition canWrite;
};

All I want to do is defining an array of Monitor::Variable<int>
objects and initializing their const mode as TIMED_WAIT, BLOCKING or
NONBLOCKING.

Monitor::Variable<int> datad[] = {TIMED_WAIT, NONBLOCKING, BLOCKING,
TIMED_WAIT}; // Compiler doesn't give any error, here

But it complains like that:

------------------------------------------------------------
**** Build of configuration Debug for project test ****

make all
Building file: ../main.cpp
Invoking: GCC C++ Compiler
g++ -I/usr/local/boost_1_34_1 -I"/home/oktay/workspace/test" -O0 -g3 -
Wall -c -fmessage-length=0 -MMD -MP -MF"main.d" -MT"main.d" -o"main.o"
"../main.cpp"
.../main.cpp: In function 'void writer_list(int)':
.../main.cpp:237: warning: unused variable 'data1'
.../main.cpp:239: warning: unused variable 'data3'
.../main.cpp:240: warning: unused variable 'data4'
/usr/local/boost_1_34_1/boost/noncopyable.hpp: In copy constructor
'boost::condition::condition(const boost::condition&)':
/usr/local/boost_1_34_1/boost/noncopyable.hpp:27: error:
'boost::noncopyable_::noncopyable::noncopyable(const
boost::noncopyable_::noncopyable&)' is private
/usr/local/boost_1_34_1/boost/thread/condition.hpp:79: error: within
this context
/usr/local/boost_1_34_1/boost/noncopyable.hpp: In copy constructor
'boost::detail::condition_impl::condition_impl(const
boost::detail::condition_impl&)':
/usr/local/boost_1_34_1/boost/noncopyable.hpp:27: error:
'boost::noncopyable_::noncopyable::noncopyable(const
boost::noncopyable_::noncopyable&)' is private
/usr/local/boost_1_34_1/boost/thread/condition.hpp:35: error: within
this context
/usr/local/boost_1_34_1/boost/thread/condition.hpp: In copy
constructor 'boost::condition::condition(const boost::condition&)':
/usr/local/boost_1_34_1/boost/thread/condition.hpp:79: note:
synthesized method
'boost::detail::condition_impl::condition_impl(const
boost::detail::condition_impl&)' first required here
.../ReadersNWriters.h: In copy constructor
'ReadersNWriters::ReadersNWriters(const ReadersNWriters&)':
.../ReadersNWriters.h:11: note: synthesized method
'boost::condition::condition(const boost::condition&)' first required
here
.../Variable.h: In copy constructor
'Monitor::Variable<int>::Variable(const Monitor::Variable<int>&)':
.../Variable.h:13: note: synthesized method
'ReadersNWriters::ReadersNWriters(const ReadersNWriters&)' first
required here
/usr/local/boost_1_34_1/boost/noncopyable.hpp: In copy constructor
'boost::mutex::mutex(const boost::mutex&)':
/usr/local/boost_1_34_1/boost/noncopyable.hpp:27: error:
'boost::noncopyable_::noncopyable::noncopyable(const
boost::noncopyable_::noncopyable&)' is private
/usr/local/boost_1_34_1/boost/thread/mutex.hpp:35: error: within this
context
.../Variable.h: In copy constructor
'Monitor::Variable<int>::Variable(const Monitor::Variable<int>&)':
.../Variable.h:13: note: synthesized method 'boost::mutex::mutex(const
boost::mutex&)' first required here
.../main.cpp: At global scope:
.../main.cpp:27: note: synthesized method
'Monitor::Variable<int>::Variable(const Monitor::Variable<int>&)'
first required here
make: *** [main.o] Error 1
----------------------------------------------------------------

In general it's a good idea to reserve all uppercase names for macros.
Using them for constants is a Java'ism. Leads to problems in C++.

I really don't what Java'ism, C'ism or C++'ism are, and I want to
learn coding conventions about C++, if you could suggest me a good
tutorial, ebook etc.

Thanks
yatko
 
A

Alf P. Steinbach

* (e-mail address removed):
I don't know that there are agreed upon conventions regarding style,
but there are plenty of books written about using C++ the Right Way
(tm).

http://www.parashift.com/c++-faq-lite/how-to-learn-cpp.html#faq-28.5

Unfortunately it seems the FAQ hasn't been updated for a while.

For the OP's specific question, a good book about conventions is "C++
Coding Standards: 101 Rules, Guidelines, and Best Practices" by Herb
Sutter and Andre Alexandrescu.

They have some apparently silly advice about reinterpret_cast, but apart
from that very very solid (unfortunately I don't have this book).



Cheers, & hth.,

- Alf
 
A

Andrey Tarasevich

Alf said:
In early C one had to use macros for constants,
so constants also typically had all uppercase names.
> ...

Just as a side note, aside from enums, modern C still relies on macros
for true constants (as in "constant expressions"). Even in C99
const-qualified objects are still not "constants", i.e. they can't be
used in constant expressions.
 
I

InsainFreak101

Hi all;

I want to define an array of objects and initialize them, but I don't
know how I could do that simply. I have searched over net, and have
found a few solutions. Does anybody has a better, simple solution?

Suppose there is a Foo class that has const member.

class Foo
{
public:
Foo(int, int);
~Foo();

private:
const int ID;
double int var;

};

First method:

Foo objects [MAX] = {Foo(0,1),Foo(1,2),Foo(2,3)};

// This one requires copy constructor, but I have const members, so it
doesn't work for me.

Second method:

std::vector<Foo> objects;

objects[0] = * (new Foo(0,1));
objects[1] = * (new Foo(1,2));
objects[2] = * (new Foo(2,3));

//This one solves problem of const members, but I dont want to use
vector.

I want to initialize the array as following, but it doesn't work.

Foo objects[MAX] = {{0,1},{1,2},{2,3}};

Thanks

yatko

the way you have it should work exept you can't change a constant
after it is inisialized and you should write it:

Foo objects[MAX] = {Foo(0,1),Foo(1,2),Foo(2,3)};

someone else praly suggested this, but just the same, hope this helps
 
J

James Kanze

* yatko:
I want to define an array of objects and initialize them,
but I don't know how I could do that simply. I have searched
over net, and have found a few solutions. Does anybody has a
better, simple solution?
Suppose there is a Foo class that has const member.
class Foo
{
public:
Foo(int, int);
~Foo();
private:
const int ID;
double int var;
};
First method:
Foo objects [MAX] = {Foo(0,1),Foo(1,2),Foo(2,3)};
This method works nicely.

For his class above, yes. As he later indicates, he was holding
out on us---his actual class contains a boost::mutex, which
isn't copiable.
It's opposite: the method below requires copy constructor,
while the one above doesn't.

No. They both require the copy constructor. To begin with,
aggregate initialization is copy initialization. And even if it
weren't, his initialization expression in both cases has the
type Foo, so the copy constructor will be called.

Of course, the presence of a const member doesn't cause any
problems for the copy constructor. His class above has a
perfectly good, compiler generated copy constructor.
Second method:
std::vector<Foo> objects;
objects[0] = * (new Foo(0,1));
objects[1] = * (new Foo(1,2));
objects[2] = * (new Foo(2,3));
//This one solves problem of const members, but I dont want to use
vector.
This doesn't work for const members.

Right, because is uses assignment, and not copy construction.
I want to initialize the array as following, but it doesn't work.
Foo objects[MAX] = {{0,1},{1,2},{2,3}};
Works OK.

Huh? Foo is not an aggregate, and so cannot be initialized
using aggregate initialization. To initialize the array, you
must provide a list of Foo, or of things which can be implicitly
converted to Foo---since Foo doesn't have a converting
constructor, there isn't anything which can be converted to Foo.
 
J

James Kanze

* (e-mail address removed):

The Java convention (probably) stems from the C convention to
use all uppercase for macros. In early C one had to use
macros for constants, so constants also typically had all
uppercase names. But it's been some years since 1972, and C++
gives the ability to define constants without using macros.

It was the convention in early C++ as well. We've learned
better since:).

A small historical note: in early C, I'm not sure that the
conventions were that fixed. There was a convention that macros
which could be used as a function obeyed the same naming
conventions as a function. There was also a convention that
manifest constants were all caps---and manifest constants were
(and still are) implemented by means of a macro in C. Beyond
that, it was pretty open---putc was almost always a macro, for
example, and arguable was distinct from a function, because it
evaluated its FILE* argument twice. (Exactly how close to a
function "used as a function" implied was generally not stated.)

When I started using C++ professionally, the convention was
still generally all caps for constants. But the first questions
were being raised; Jon Bentley (I think) had a quote along the
lines of "one man's constant is another man's variable", and
real applications were encountering the case where evolution
caused a "constant" to be read from a configuration file. I
remember being among the first in my company to argue against
all caps for constants.

Of course, that was all a very long time ago (for this
profession, anyway). I'd say that the all cap's for macros has
been pretty well established for at least ten years now, maybe
more. And we have a case of Java enshrining something that was
already beginning to be recognized as a mistake in C++.
Since macros don't respect scopes it's generally not a good
idea to let non-macro names enter that "namespace" in C++
(note: Java doesn't have this problem, because Java doesn't
have a preprocessor).

But even in Java, one man's constants are another man's
variables. And the Java library itself doesn't adhere strictly
to the rule (probably because some constants later became
variables, and vice versa---although it's hard to imagine that
Color::red was ever anything but a constant).
 
A

Alf P. Steinbach

* James Kanze:
* yatko:
I want to define an array of objects and initialize them,
but I don't know how I could do that simply. I have searched
over net, and have found a few solutions. Does anybody has a
better, simple solution?
Suppose there is a Foo class that has const member.
class Foo
{
public:
Foo(int, int);
~Foo();
private:
const int ID;
double int var;
};
First method:
Foo objects [MAX] = {Foo(0,1),Foo(1,2),Foo(2,3)};
This method works nicely.

For his class above, yes. As he later indicates, he was holding
out on us---his actual class contains a boost::mutex, which
isn't copiable.
It's opposite: the method below requires copy constructor,
while the one above doesn't.

No. They both require the copy constructor. To begin with,
aggregate initialization is copy initialization.

You're right, I didn't know that. It's not very logical and MSVC 7.1
handles lack of copy constructor fine (which is logical, it's not
technically needed except when initializer is an object of a derived
class, only formally, and only a very obnoxious compiler would actually
do that unnecessary copying!). Though now that you mentioned it, I
checked and g++ detects the formal problem, and Comeau does, and I found
it hidden away in §12.6.1/2 and also §8.5/12.

Have you any idea why on earth that rule is there, what problem it's
meant to help us avoid? I can understand that it helps with the
standard-writer's internal problem of specifying the notation, the
special case of derived class object as initializer. But, it's
completely in breach of the "don't pay for what you're not using"
principle of C++, and the C++0x fix of 'T const&' argument semantics.

So, it looks like a defect to me: if I want that array of
non-copy-constructible objects, the compiler should let me have it
(without resorting to placement new), as MSVC does...


[snip]
I want to initialize the array as following, but it doesn't work.
Foo objects[MAX] = {{0,1},{1,2},{2,3}};
Works OK.

Huh?

Sorry. I didn't see that it was different. Again you're right.


Cheers,

- Alf
 
J

James Kanze

* James Kanze:
You're right, I didn't know that. It's not very logical and
MSVC 7.1 handles lack of copy constructor fine (which is
logical, it's not technically needed except when initializer
is an object of a derived class, only formally, and only a
very obnoxious compiler would actually do that unnecessary
copying!). Though now that you mentioned it, I checked and
g++ detects the formal problem, and Comeau does, and I found
it hidden away in §12.6.1/2 and also §8.5/12.
Have you any idea why on earth that rule is there, what
problem it's meant to help us avoid?

Not really. The real question, to begin with, is why there are
two types of initialization. I'm pretty sure that there must be
a strong reason for that, because it did make a lot of extra
work for the committee. It would have been a lot easier to just
say that initialization calls the corresponding constructor,
period. (There is some argument in that you might not want to
support conversions, e.g. of function arguments. But the
distinction predates explicit, so that aspect wasn't what
motivated it.)

Once you accept that:
T obj( init ) ;
and
T obj = init ;
have different semantics, of course: the reason aggregate
initialization has the semantics of the second is because its
syntax has an =.

I know, it's not a very good reason at all. But I can't think
of another one. Fundamentally, I'm with you on this; it would
seem logical to me that something like:
T array[2] = { init1, init2 } ;
be the equivalent of:
T array[0]( init1 ) ;
T array[1]( init2 ) ;
as far as initialization is concerned.

I wasn't able to find anything about this in Stroustrup's DEC++,
either (but maybe I just didn't know where to look). The
distinction between the two types of initialization is present
in the ARM, but only for class types; the ARM doesn't say which
type of initialization applies to classes in aggregates when
aggregate initialization is used.

I'd suggest we take it up in csc++, except that that group
doesn't seem to be any more.
I can understand that it helps with the standard-writer's
internal problem of specifying the notation, the special case
of derived class object as initializer. But, it's completely
in breach of the "don't pay for what you're not using"
principle of C++, and the C++0x fix of 'T const&' argument
semantics.
So, it looks like a defect to me: if I want that array of
non-copy-constructible objects, the compiler should let me
have it (without resorting to placement new), as MSVC does...

It's not a defect in the formal sense: something that is poorly
specified, contradictory, or doesn't correspond to the voted
intent. I would consider it perhaps a poor decision, however.

In practice, I don't think it a big issue. Agglomerates don't
usually make sense except with value types, value types should
support copy, and the compiler will optimize the copy out, so
the net effect is what we want. Still, it isn't pretty.
 

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
474,265
Messages
2,571,069
Members
48,771
Latest member
ElysaD

Latest Threads

Top