Classes and Default Constructors

G

gordon.is.a.moron

Hello,

I have a few questions regarding classes in C++. First of all I have
two classes. I have a default constructor that assigns default values:

//class D.h
class D
{
public:
D(int id = 0, int sn =0);

}


I want to associate Class D with Class E:

//class E.h
class E
{
private:
D instance_;
}

//class E.cpp
{
void E::SendData()
{
D instance_(50.12);
}
}

However, the D instance_ delcaration calls the default constructor
with default values. So as well as calling the constructor in a
function in class E, the default constructor is called first with
default values. What is the correct way of associating an instance
with the other object without invoking the default constructor? The
isue is I can't really set those parameters in the declaration, and
I'm not sure I really should anyway, it doesn't seem correct.

And in a related question, I'm not entirely clear when you would call
D instance_(50,12) and D *instance_ = new D(50,12). I was doing this
initially as I thought using pointers would be more efficient over
references, but I'm not sure this is true. As far as I can gather I
should use references as much as possible and only use pointers when I
need to change what I'm pointing to, since I can't "move" a reference.
And also I would guess when I want to create an indeterminate number
of objects at runtime dynamically, which would presumably force me to
use pointers as this is what new returns in C++ when creating objects
of classes.

Regards,

Gordy.
 
J

James Kanze

I have a few questions regarding classes in C++. First of all
I have two classes. I have a default constructor that assigns
default values:
//class D.h
class D
{
public:
D(int id = 0, int sn =0);
}

You're missing a ';'. It's generally preferable to post
compilable code.
I want to associate Class D with Class E:

What do you mean by "associate"? There are lots of different
types of association.
//class E.h
class E
{
private:
D instance_;
}
Ditto.

//class E.cpp
{
void E::SendData()
{
D instance_(50.12);
}
}

And I don't understand this at all. What are the outer braces
doing? And more importantly, what is void E::SendData(). If
it's a member function, then instance_ is a local variable, and
the fact that it is in a member function of E is largely
irrelevant.
However, the D instance_ delcaration calls the default
constructor with default values.

No it doesn't. It calls D::D( 50, 0 ), only using the default
value for the missing second argument. (It also truncates the
floating point first argument. Perhaps you meant to write:

D instance_( 50, 12 ) ;

In that case, it will call D::D( 50, 12 ).
So as well as calling the constructor in a function in class
E, the default constructor is called first with default
values.

If you specify values in the definition, the compiler will call
the corresponding constructor.
What is the correct way of associating an instance
with the other object without invoking the default
constructor?

What do you mean by "associating"? If the object is a base
class or member, you use the initializer sequence in the
constructor. Otherwise, you provide initializers in the
statement which creates the object; in the definition or the new
expression.
The isue is I can't really set those parameters in the
declaration, and I'm not sure I really should anyway, it
doesn't seem correct.

Why can't use set them in the definition? If you don't know
them at that moment, perhaps the definition is appearing too
early.
And in a related question, I'm not entirely clear when you
would call D instance_(50,12) and D *instance_ = new D(50,12).

You use the first when you want static or automatic lifetime,
the second when you want dynamic lifetime. If the object has
copy semantics, it's generally preferable to stick to automatic
lifetime, copying it as necessary. At least until the profiler
tells you otherwise.
I was doing this initially as I thought using pointers would
be more efficient over references, but I'm not sure this is
true.

Given that all compilers I know implement references as
pointers, any differences are likely to be slight, favoring
references (since the compiler might be able to optimize a
little better knowing that the value can't be null).

But how is there relevant to any of the above? Nothing you've
posted involves references in the least.
As far as I can gather I should use references as much as
possible and only use pointers when I need to change what I'm
pointing to, since I can't "move" a reference.

That's part of what is probably the most frequent rule. Another
part is that you use pointers if you need a possible null
pointer. And most coding guidelines seem to recommend pointers
when changing ownership; for whatever reasons, most people don't
seem to like having to do "delete &someReference". But in the
end, it's largely a question of local coding conventions, except
in the cases where references don't work.
And also I would guess when I want to create an indeterminate
number of objects at runtime dynamically, which would
presumably force me to use pointers as this is what new
returns in C++ when creating objects of classes.

It's possible to do this with references, as well, but probably
not very idiomatic. Most of the time, you'll either want a null
pointer (to indicate a dead end in the navigation branch), or
you'll need to reseat the pointers (since if there are no
dead-ends, the graph must contain cycles).
 
G

gordon.is.a.moron

When E::SendData is called, you are declaring a new object named
'instance_' and constructing it with the values provided (I'm assuming
here that you meant to type "50,12" not "50.12".) This new object is not
associated with the member-variable E::instance_ in any way.


Since a D object must be constructed when the E object gets constructed,
then the most obvious solution is to provide the parameters in the E
object's constructor, like this:

class E {
   D instance_;
public:
   E():instance_(50, 12) { }
   void SendData() {
      // use 'instance_' here.
   }

};

If you don't know what the values will be until runtime (i.e., if you
are using variables for the values and the variables aren't set until
after the programs been running for a bit,) then you need to use new to
avoid the default construction, like this:

class E {
   D* instance_;
public:
   E(): instance_(0) { }
   ~E() { delete instance_ }
   E(const E& o): instance_(0) {
      if (o.instance_)
         instance_ = new D(*o.instance_);
   }
   void operator=(const E& o) {
      E tmp(o);
      swap(tmp.instance_, instance_);
   }
   void SendData(int x, int y) {
      delete instance_;
      instance_ = new D(x, y);
      // use 'instance_' here.
   }

};

Yes, all that extra code is necessary if you want copy construction and
assignment to work properly.

I may have to do this. The "real code" is actually creating a custom
event (class) in a third party library. The first parameter is an
event type rather than an int (the second is an int ID for the event
table). I probably should've left that in, but I just tried to
simplify it. There are various ways to do it, but using the suggestion
below (i.e. no copy ctor) doesn't work at the moment.
Unless you have a very powerful reason to avoid the default
construction, it would be easier to go ahead and let the default
construction happen. Then the code would look like this:

I don't really, the below code is what I was intending. I just thought
it was inefficient to call the default ctor when I didn't need to.
class E {
   D instance_;
public:
   void SendData(int x, int y) {
      instance_ = D(x, y);
   }

};

Note in the above code how I assigned to instance_ rather than declaring
a variable named instance_ as you did in your code.

Good point. I have discussed this with users of the library. They
reckoned that it's unusual to associate the event class with the
client classes as the event shouldn't be modified after being fired.
However, I though an association (in the OO sense) applied to one
object calling another on it's behalf. I call set methods after
creation in one class (i.e. I populate data in the event class in
sendata) and I will eventually get that data using an accessor in the
other client class that the event is being fired at. This is where I
got the idea of the associations. I would guess that the event should
not be associated and the event system should handle this. I suppose
the point of en event system it that it is de-coupled to an extent. I
know that is OO rather than C++, but I thought I'd throw it in...

My mistake, yes you're right that should be 50,12 not 50.12.
Your example doesn't compare references to pointers. 'D instance_(50,
12)' defines an object, not a reference to an object. I suggest you
avoid using new whenever you can. It makes the code much easer to
understand and write in most cases.

Ah, thanks. I think you have cleared up a misunderstanding I had for a
while. That certainly makes it much more clear. In Java creating an
object returns what they refer to as a refernce (though I've since
read that it is actually more like a pointer). So I was under the
impression a reference was returned in my example, and a pointer would
be returned using new. Obviously the former is incorrect.
References were invented to provide more options when passing parameters
to functions and returning values from functions. I suggest you don't
use references in other contexts.

Ok, thanks.
I suggest you use one of the standard containers when you have to do the
above, usually vector.

Ok, I will use that (or one of the lib methods provided).

Much appreciated, thankyou.

Gordy.
 

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

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top