Constructor of a derived class

M

Manuel

The parent is an abstract class, with default implicit constructor:

-----------------------------------
class mhwidget
{
public:
virtual void draw()= 0;
virtual void setPosition(GLint, GLint)= 0;
virtual void setWidth(GLint)= 0;
virtual void setHeight(GLint)= 0;
virtual bool isMouseOver(GLint, GLint)= 0;
virtual ~mhwidget() {}
};
------------------------------------

The derived class I've written use a simple constructor, because the
parent have the default constructor. And without destructor. It's OK?

------------------------------------
class square : public mhwidget
{
private:
GLint positionX;
GLint positionY;
GLint width;
GLint height;


public:
square(int posX, int posY, GLint w, GLint h);
virtual void setPosition(GLint posX, GLint posX);
virtual void setWidth(GLint w);
virtual void setHeight(GLint h);
virtual bool isMouseOver(GLint posX, GLint posX);
virtual void draw();
};
------------------------------------

Besides, note that I use the same variables names in different functions
(posX and posY are in both constructor and setPosition, w and h are in
constructor, setWidth and setHeight. However they should be ok, because
they work loacally into the functions...it's ok too?



thanks,

Manuel
 
V

Victor Bazarov

Manuel said:
The parent is an abstract class, with default implicit constructor:

-----------------------------------
class mhwidget
{
public:
virtual void draw()= 0;

Shouldn't 'draw' have an argument? Maybe not...
virtual void setPosition(GLint, GLint)= 0;
virtual void setWidth(GLint)= 0;
virtual void setHeight(GLint)= 0;
virtual bool isMouseOver(GLint, GLint)= 0;
virtual ~mhwidget() {}
};

Looks fine. Except that I would probably make 'isMouseOver' a 'const'
function:

virtual bool isMouseOver(GLint, GLint) const = 0;

unless, of course, it modifies the object for some reason.

Well, it's not without the destructor. It has compiler-provided d-tor,
and that d-tor is virtual.
------------------------------------
class square : public mhwidget
{
private:
GLint positionX;
GLint positionY;
GLint width;
GLint height;


public:
square(int posX, int posY, GLint w, GLint h);
virtual void setPosition(GLint posX, GLint posX);
virtual void setWidth(GLint w);
virtual void setHeight(GLint h);
virtual bool isMouseOver(GLint posX, GLint posX);

Same note about the constness of it.
virtual void draw();
};
------------------------------------

Besides, note that I use the same variables names in different functions
(posX and posY are in both constructor and setPosition, w and h are in
constructor, setWidth and setHeight. However they should be ok, because
they work loacally into the functions...it's ok too?

Yes, argument names are local to the functions. You can have same names
of arguments in different functions, they won't interfere with each other.

V
 
L

Luke Meyers

Manuel said:
The parent is an abstract class, with default implicit constructor:

-----------------------------------
class mhwidget
{
public:
virtual void draw()= 0;
virtual void setPosition(GLint, GLint)= 0;
virtual void setWidth(GLint)= 0;
virtual void setHeight(GLint)= 0;
virtual bool isMouseOver(GLint, GLint)= 0;
virtual ~mhwidget() {}
};
------------------------------------

The derived class I've written use a simple constructor, because the
parent have the default constructor. And without destructor. It's OK?

------------------------------------
class square : public mhwidget
{
private:
GLint positionX;
GLint positionY;
GLint width;
GLint height;


public:
square(int posX, int posY, GLint w, GLint h);
virtual void setPosition(GLint posX, GLint posX);
virtual void setWidth(GLint w);
virtual void setHeight(GLint h);
virtual bool isMouseOver(GLint posX, GLint posX);
virtual void draw();
};
------------------------------------

Besides, note that I use the same variables names in different functions
(posX and posY are in both constructor and setPosition, w and h are in
constructor, setWidth and setHeight. However they should be ok, because
they work loacally into the functions...it's ok too?

You're mostly okay here. Using variables of the same name in totally
different scopes is no problem at all. You should not declare the
methods in the derived class to be virtual, unless you intend to
inherit further from it. If you do, you'll need to make the destructor
virtual as well. A good compiler will provide a warning along these
lines (virtual methods but non-virtual dtor).

If you don't explicitly call the base class constructor (as the first
item in your ctor's initializer list), the default one gets called
automatically. That's what you want, so you're fine.

isMouseOver() and draw() should probably both be const.

Luke
 
M

Manuel

Victor said:
Looks fine. Except that I would probably make 'isMouseOver' a 'const'

Thanks (to Luke too).
Now a very beginner question (sorry but this must be so basic that no
one of text I've talk about it...).

Without constructor I can create and use the square directly, in example
to push it into a container:

contn.addWidget(new square);

now, with constructor, should I write

contn.addWidget(new square(10,10,100,100));

??


thx,

Manuel
 
B

Ben Pope

Manuel said:
Thanks (to Luke too).
Now a very beginner question (sorry but this must be so basic that no
one of text I've talk about it...).

Without constructor I can create and use the square directly, in example
to push it into a container:

contn.addWidget(new square);

now, with constructor, should I write

contn.addWidget(new square(10,10,100,100));

Yes.

But be careful, if contn is a standard container, then you will have to
iterate over that container and delete all the pointers it contains. If
an exception is thrown before you do that, you have a memory leak.

Ben Pope
 
V

Victor Bazarov

Manuel said:
Thanks (to Luke too).
Now a very beginner question (sorry but this must be so basic that no
one of text I've talk about it...).

Without constructor

You have to remember that it's not "without constructor". It's "without
a user-defined constructor". The compiler adds the default constructor if
it can, and that's what is used to create the object.
> I can create and use the square directly, in example
to push it into a container:

contn.addWidget(new square);

now, with constructor, should I write

contn.addWidget(new square(10,10,100,100));

??

Yes, as soon as you declare/define your own constructor, the compiler does
not generate its own any more.

V
 
G

Guest

Manuel said:
now, with constructor, should I write

contn.addWidget(new square(10,10,100,100));
>

I won't answer how to do it right according to your requirements and
assumptions related to the project, but only from technical point of
view, from point of C++.

class square : public mhwidget
{
private:
GLint positionX;
GLint positionY;
GLint width;
GLint height;
public:
// Default ctor initializes square with zeros
square() : positionX(0), positionY(0), width(0), height(0) {}
square(int posX, int posY, GLint w, GLint h);
};

Now, you can add square initialized with zeros:
contn.addWidget(new square());

Note: as I said, I don't know what are your requirements,
so you have to decide if zeros-based-square is valid according to the
logic of your app.

Cheer
 
M

Manuel

Mateusz said:
I won't answer how to do it right according to your requirements and
assumptions related to the project, but only from technical point of
view, from point of C++.

You know I'm a C++ beginner, so any help and suggestion is welcome.
:)
Now, you can add square initialized with zeros:
contn.addWidget(new square());

Thanks!

Ben said:
>But be careful, if contn is a standard container, then you will have to
>iterate over that container and delete all the pointers it contains.
>If an exception is thrown before you do that, you have a memory leak.

I've already asked this in another post (without reply), so I copy my
question here too:

-----------------------------------------------------------------
To delete all widgets, I'written the function deletaAll(). This is the
full code of container of widgets. Really I'm not sure it work: I've
modified an example (thanks to Mateusz) that worked, but I've not fully
understand functors, so maybe I've ruined all, using them in a function.
What do you think? Maybe OK?

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

#include <windows.h>
#include "mhcontainer.h"


#include <iostream>//For debug

struct DeleteObjs
{
template <typename T>
void operator()(const T* ptr) const
{
delete ptr;
}
};

//Put widget into container
void mhcontainer::addWidget(mhwidget* w)
{
std::cout << "added";
widgetList.push_back(w);
std::cout << "now the size is: " << widgetList.size() << std::endl ;
}

//Draw all widgets
void mhcontainer::drawAll()
{

std::vector<mhwidget*>::iterator it = widgetList.begin();
while(it != widgetList.end())
{
std::cout << "widget!";
(*it++)->draw();
//(*it++)->tryMe();
}
}

//Delete all widgets
void mhcontainer::deleteAll()
{
std::for_each(widgetList.begin(), widgetList.end(), DeleteObjs());
}
 
B

Ben Pope

Manuel said:
I've already asked this in another post (without reply), so I copy my
question here too:

-----------------------------------------------------------------
To delete all widgets, I'written the function deletaAll(). This is the
full code of container of widgets. Really I'm not sure it work: I've
modified an example (thanks to Mateusz) that worked, but I've not fully
understand functors, so maybe I've ruined all, using them in a function.
What do you think? Maybe OK?

It depends on mhcontainer and whether any exceptions are thrown before
that container goes out of scope.

You could call deleteAll from the destructor of mhcontainer, that would
probably make sure it's correct, but since I don't have all the details
of mhcontainer to hand, I can't tell you.

Ben Pope
 
B

Bob Hairgrove

You should not declare the
methods in the derived class to be virtual, unless you intend to
inherit further from it. If you do, you'll need to make the destructor
virtual as well.

If they are virtual in the base class, then they are also virtual in
the derived classes, whether or not the keyword "virtual" is used
there or not (see section 10.3, paragraph 2 of the C++ standard).
 
G

Guest

Manuel said:
I've not fully understand functors, so maybe I've ruined all, using
them in a function. What do you think? Maybe OK?

It looks OK. But you have to remember to call mhcontainer::deleteAll()
in somewhere. Where? You have to decide because it.
It depends on many details about logic/requirements/flow/objects
lifetime/etc. in your app, which you are supposed to know.

Cheers
 
L

Luke Meyers

Bob said:
If they are virtual in the base class, then they are also virtual in
the derived classes, whether or not the keyword "virtual" is used
there or not (see section 10.3, paragraph 2 of the C++ standard).

That surprises me. I don't have a copy of the standard handy, so I beg
your indulgence for a moment while I check an implication of this. If
I have the following code, where a derived class hides a parent's
non-virtual method, like so:

class Parent {
void foo();
};

class Child : public Parent {
void foo();
};

A call to foo() from a Parent* will of course call Child::foo(). But
then if I introduce a new superclass, like so:

class Grandparent {
virtual void foo();
};

class Parent : public Grandparent { // replace previous definition with
this
void foo();
};

Now the same call mentioned above will dispatch to Child::foo()? I
realize that to permit the original setup would be questionable design,
but... is this really how it works? Seems icky.

Luke
 
B

Bob Hairgrove

That surprises me. I don't have a copy of the standard handy, so I beg
your indulgence for a moment while I check an implication of this. If
I have the following code, where a derived class hides a parent's
non-virtual method, like so:

class Parent {
void foo();
};

class Child : public Parent {
void foo();
};

A call to foo() from a Parent* will of course call Child::foo().

No. It calls Parent::foo() (assuming that foo() is accessible, which
they aren't as written above). Example:

// test_parent.cpp
#include <iostream>
#include <ostream>

struct Parent {
void foo() { std::cout << "Parent::foo()" << std::endl; }
};

struct Child : public Parent {
void foo() { std::cout << "Child::foo()" << std::endl; }
};

int main()
{
Child c;
Parent * p(&c);
p->foo();
}

Change Parent::foo() to be virtual, then it does call Child::foo().
But
then if I introduce a new superclass, like so:

class Grandparent {
virtual void foo();
};

class Parent : public Grandparent { // replace previous definition with
this
void foo();
};

Now the same call mentioned above will dispatch to Child::foo()?

Yes, if you make foo() accessible.
I
realize that to permit the original setup would be questionable design,
but... is this really how it works? Seems icky.

It's really very elegant: you can have objects of three different
types, as above, yet store pointers to all three in a container of
Grandparent pointers. Calling foo() on each pointer calls a different
foo() for each object.
 
L

Luke Meyers

Bob said:
No. It calls Parent::foo() (assuming that foo() is accessible, which
they aren't as written above). Example:

I'm very sorry, I mistyped and led you down the wrong path. I meant of
course that, because Parent::foo() is non-virtual, a call to foo() from
a Parent* will "of course" call Parent::foo().
Yes, if you make foo() accessible.

But if I don't change the signature of Parent::foo() to be virtual?
That's what I was asking. To be explicit, here's a full scenario:

struct Grandparent { virtual void foo(); };
struct Parent : Grandparent { void foo(); }; // Intentionally
non-virtual
struct Child : Parent { void foo(); };

int main() {
Grandparent* g = new Child();
Parent* p = new Child();
g->foo(); // (1)
p->foo(); // (2)
return 0;
}

Okay, so which foo gets called for each of (1) and (2) above? My
understanding prior to this thread was that both would dispatch to
Parent::foo(), since the "chain of virtualization" stops there. If
that's not the case, I would say it's not elegant at all, since it
allows something outside of Parent's control (Grandparent) determine
the behavior of Parent and its subclasses.

Sorry for the earlier misunderstanding.

Luke
 
B

Bob Hairgrove

To be explicit, here's a full scenario:

struct Grandparent { virtual void foo(); };
struct Parent : Grandparent { void foo(); }; // Intentionally
non-virtual
struct Child : Parent { void foo(); };

int main() {
Grandparent* g = new Child();
Parent* p = new Child();
g->foo(); // (1)
p->foo(); // (2)
return 0;
}

Okay, so which foo gets called for each of (1) and (2) above?

Well, did you try it out?

BTW:
(1) You have leaked memory twice in the above code by not
deleting the objects created with new;
(2) If you delete them, you have undefined behavior because you
didn't declare (and define) a virtual destructor for Grandparent.
(3) If you define a virtual destructor for Grandparent and delete
the objects correctly, the code still wouldn't be exception safe
unless you either use smart pointers for these objects, or wrap
each call to new in two "try" blocks, each with its own "catch"
handler.

You should prefer to create objects on the stack unless you have a
strong reason not to.
My understanding prior to this thread was that both would dispatch to
Parent::foo(), since the "chain of virtualization" stops there. If
that's not the case, I would say it's not elegant at all, since it
allows something outside of Parent's control (Grandparent) determine
the behavior of Parent and its subclasses.

It's not elegant to you simply because you expected it to work
differently than it actually does. ;)

Subclasses of Parent need to know that Parent inherits from
Grandparent. In order for the Parent class to compile, it needs to
have the source code for Grandparent. There it says that foo() is
virtual. The same applies to Parent. If Parent needs different
behavior, it should provide a different function which doesn't
override Grandparent::foo().

But you can still control which version of foo() is called by
explicitly qualifying the function name:

#include <iostream>
#include <ostream>

struct Grandparent {
virtual void foo() {
std::cout << "Grandparent::foo()" << std::endl; }
// just in case:
virtual ~Grandparent() {}
};

struct Parent : public Grandparent {
void foo() { std::cout << "Parent::foo()" << std::endl; }
};

struct Child : public Parent {
void foo() { std::cout << "Child::foo()" << std::endl; }
};

int main() {
Child obj1, obj2;
Grandparent *g(&obj1);
Parent *p(&obj2);
g->foo(); // calls Child::foo()
p->foo(); // calls Child::foo()
p->Parent::foo(); // calls Parent::foo()
p->Grandparent::foo(); // calls Grandparent::foo()
// g->Parent::foo();
// ERROR:
// Grandparent knows nothing of Parent!
return 0;
}

As long as you stick to the appropriate interface, there's no problem
in differentiating between implementations. But usually one doesn't
care which one is called. It's up to the *object* to know, not the
*caller* of the object. It's the OOP way of doing things!
 

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,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top