BorlandTalk.com Forum Index BorlandTalk.com
Borland discussion newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

static members in derived classes

 
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> C++ Builder (Language C++)
View previous topic :: View next topic  
Author Message
Stephane Baillargeon
Guest





PostPosted: Sat Apr 21, 2007 7:35 pm    Post subject: static members in derived classes Reply with quote



Hello,

This is what I want to know. Is it good practice to use a global class
to derive from for storing public vars (static)?

File1.h
-------
class global
{
private:
static int* pnum;
static string str;
public:
global();
~global();
set_pnum( int* in_pnum );
set_str( string in_str );
};

File2.h
-------
#include "File1.h"

class river : public global
{
public:
river();
~river();
connect();
}

File2.cpp
---------
#include "File2.h"

river::river();
{
}
river::~river();
{
}
river::connect();
{
CONNECTION con = CreateCon( pnum, str ); // vars from global
}

main.cpp
--------
#include "File2.h"

int main()
{
int inum = 55;
string str = "Hello";

global* pglob = new global();
pglob->set_pnum( &inum );
pglob->set_str( str );

river* priv = new river();
priv->connect();
delete priv;

delete pglob;

return 0;
}
Back to top
Clayton Arends
Guest





PostPosted: Sun Apr 22, 2007 6:22 am    Post subject: Re: static members in derived classes Reply with quote



Quote:
Is it good practice to use a global class to derive from for storing
public vars (static)?

It's definitely a practice Wink. Whether it's *good* or not is usually
subjective. There are a lot OO purists that frown upon global variables. I
would say use the global if you absolutely cannot achieve the desired result
in any other way that makes sense.

Without knowing your design requirements I have to say that a global is not
required in this situation. This might be because this is just example code
but I suspect your actual design probably wouldn't need it either. If you
can show some of your design or code it might help me change my assessment.

- Clayton
Back to top
Thomas Maeder [TeamB]
Guest





PostPosted: Sun Apr 22, 2007 8:10 am    Post subject: Re: static members in derived classes Reply with quote



Stephane Baillargeon <stephane@premiere-supplies.com> writes:

Quote:
This is what I want to know. Is it good practice to use a global
class to derive from for storing public vars (static)?

I don't think that it is a good practice.


Quote:
File1.h
-------
class global
{
private:
static int* pnum;
static string str;
public:
global();
~global();
set_pnum( int* in_pnum );
set_str( string in_str );

You very probably want these to be static.


Quote:
};

So far so good. *If* the data held by that class is related in some
way; and *if* the class gets a more specific name, indicating what
that relation is.

One single class holding a hodge-podge of unrelated data only creates
a mess, though.


Quote:
File2.h
-------
#include "File1.h"

class river : public global

What's the motivation for this inheritance relationship?
Back to top
Stephane Baillargeon
Guest





PostPosted: Sun Apr 22, 2007 8:10 am    Post subject: Re: static members in derived classes Reply with quote

Clayton Arends wrote:
Quote:
Is it good practice to use a global class to derive from for storing
public vars (static)?

It's definitely a practice Wink. Whether it's *good* or not is usually
subjective. There are a lot OO purists that frown upon global variables. I
would say use the global if you absolutely cannot achieve the desired result
in any other way that makes sense.

Without knowing your design requirements I have to say that a global is not
required in this situation. This might be because this is just example code
but I suspect your actual design probably wouldn't need it either. If you
can show some of your design or code it might help me change my assessment.

- Clayton



Here is a deeper design description

I am using TADOConnection, TADOQuery, and TADOStoredProc objects created
dynamically. The Main form fetches database connection string details in
the windows registry and stores the string in an AnsiString that is a
public member of the Main form class. It also contains a map of SQL file
references (*.sql) and a localization map both are of type
std::map<int, AnsiString>.

Many other classes and forms I created need this string and these two
maps passed to them without having to do it physically. This I found can
only be achieved through static variables contained within a base class
that all classes I use are derived from it.

I don't want to access the Main form's variables (Overhead and a lot of
include directives) Each time I need one of the vars within a class or
form, I simply would create a new instance of the global object that
already has those static variables initialized.

Ex:

form1 has the following declared as public in the header:
----------
AnsiString m_strCon;
std::map<int, AnsiString> m_mapLoc;
std::map<int, AnsiString> m_mapSQL;

class1, class2, and class3 needs these vars...
form2 and form3 also need them...

form2 is declared in form1
class1 is declared in form2
form3 is declared in form2
class2 and class3 is declared in form3

Tell me what you think...

Thanks.
Back to top
Clayton Arends
Guest





PostPosted: Sun Apr 22, 2007 6:56 pm    Post subject: Re: static members in derived classes Reply with quote

Quote:
Many other classes and forms I created need this string and these two maps
passed to them without having to do it physically.

Okay, however I think the key indicator in that sentence to me that a global
might not be the best approach is your use of the word "having".

I understand the problems with getting commonly used data to lots of
classes. At times it can be a design headache. In these situations you
need to believe in and practice preferred structural techniques otherwise
your code can turn into a spaghetti mess. More on that later.

Quote:
This I found can only be achieved through static variables contained
within a base class that all classes I use are derived from it.

There is no need to have the global data be contained in a base class. For
instance, the class you showed didn't even have to have an object:

class global
{
private:
static int* pnum;
static string str;

global(); // disabled -- this class should not be constructed

public:
static int* get_pnum();
static string get_str();
static void set_pnum( int* in_pnum );
static void set_str( string in_str );
};

river::connect()
{
CONNECTION con = CreateCon( global::pnum, global::str );
}

// main
global::set_pnum( &inum );
global::set_str( str );

Since the data members are static they exist independent of any objects.

However, doing things in that manner is only asking for trouble. It's okay
if you have fundamental data types that manage their memory for themselves.
But, if you store any allocated data pointers that need to be freed it is
better to design a singleton class. Again, you'll encounter OO programmers
that shiver at this word (so be prepared to defend the design if it *has* to
be done this way). There are several ways to design a singleton. The
important concept is that there can only ever be one object of the singleton
class' type:

class TGlobalConnectionData
{
private:
TStream* SomeStream;

TGlobalConnectionData(); // hidden, singleton constructor

static TGlobalConnectionData* get_obj();

public:
~TGlobalConnectionData();

static TStream* get_Stream();
static void set_Stream(TStream* Value);
};

TGlobalConnectionData::TGlobalConnectionData() : SomeStream(NULL)
{
}

TGlobalConnectionData::~TGlobalConnectionData()
{
delete SomeStream;
}

TGlobalConnectionData* TGlobalConnectionData::get_obj()
{
static TGlobalConnectionData theData;
return &theData;
}

TStream* TGlobalConnectionData::get_Stream()
{
return get_obj()->SomeStream;
}

void TGlobalConnectionData::set_Stream(TStream* Value)
{
TGlobalConnectionData* obj = get_obj();
delete obj->SomeStream;
obj->SomeStream = Value;
}

// main
TStream* stream = new TMemoryStream;
TGlobalConnectionData::set_Stream(stream);

// somewhere in code
TStream* stream = TGlobalConnectionData::get_Stream();
stream->Position = 0;
// etc

Again, from what you've describe it appears to me that you don't *need* a
global but that it would make your life a lot easier. Instead of a global
you could design a class (much like the singleton above) that is a vessel
for your commonly used data. This object is managed by your main form and
passed to all other forms or classes that need its data.

class TMyAppConnectionData
{
// ...
};

// Main form .h
class TMainForm : public TForm
{
// ...

private:
TMyAppConnectionData MyAppConnectionData;

// ...
};

// construct another form that expects this object as an argument
TSubForm* subform = new TSubForm(this, &MyAppConnectionData);

// That subform constructs an object that expects the connection data as
// an argument
TMyConnectionClass mcc(&MyAppConnectionData);

If there are any concepts here that you are interested in but need
clarification on please post again.

HTH,
- Clayton
Back to top
Stephane Baillargeon
Guest





PostPosted: Sun Apr 22, 2007 7:52 pm    Post subject: Re: static members in derived classes Reply with quote

Thomas Maeder [TeamB] wrote:
Quote:
Stephane Baillargeon <stephane@premiere-supplies.com> writes:

This is what I want to know. Is it good practice to use a global
class to derive from for storing public vars (static)?

I don't think that it is a good practice.


File1.h
-------
class global
{
private:
static int* pnum;
static string str;
public:
global();
~global();
set_pnum( int* in_pnum );
set_str( string in_str );

You very probably want these to be static.


};

So far so good. *If* the data held by that class is related in some
way; and *if* the class gets a more specific name, indicating what
that relation is.

One single class holding a hodge-podge of unrelated data only creates
a mess, though.


File2.h
-------
#include "File1.h"

class river : public global

What's the motivation for this inheritance relationship?

motivation is that every time I create a new instance of the river
class, I will be able to access the static members of the global class.
I forgot to include "get" methods to retrieve the data from global class.
Back to top
Thomas Maeder [TeamB]
Guest





PostPosted: Sun Apr 22, 2007 8:17 pm    Post subject: Re: static members in derived classes Reply with quote

Stephane Baillargeon <stephane@premiere-supplies.com> writes:

Quote:
motivation is that every time I create a new instance of the river
class, I will be able to access the static members of the global
class. I forgot to include "get" methods to retrieve the data from
global class.

You don't need to derive river from global for that if you make the
setters and getters static.
Back to top
Stephane Baillargeon
Guest





PostPosted: Sun Apr 22, 2007 10:24 pm    Post subject: Re: static members in derived classes Reply with quote

Clayton Arends wrote:
Quote:
Many other classes and forms I created need this string and these two maps
passed to them without having to do it physically.

Okay, however I think the key indicator in that sentence to me that a global
might not be the best approach is your use of the word "having".

I understand the problems with getting commonly used data to lots of
classes. At times it can be a design headache. In these situations you
need to believe in and practice preferred structural techniques otherwise
your code can turn into a spaghetti mess. More on that later.

This I found can only be achieved through static variables contained
within a base class that all classes I use are derived from it.

There is no need to have the global data be contained in a base class. For
instance, the class you showed didn't even have to have an object:

class global
{
private:
static int* pnum;
static string str;

global(); // disabled -- this class should not be constructed

public:
static int* get_pnum();
static string get_str();
static void set_pnum( int* in_pnum );
static void set_str( string in_str );
};

river::connect()
{
CONNECTION con = CreateCon( global::pnum, global::str );
}

// main
global::set_pnum( &inum );
global::set_str( str );

Since the data members are static they exist independent of any objects.

However, doing things in that manner is only asking for trouble. It's okay
if you have fundamental data types that manage their memory for themselves.
But, if you store any allocated data pointers that need to be freed it is
better to design a singleton class. Again, you'll encounter OO programmers
that shiver at this word (so be prepared to defend the design if it *has* to
be done this way). There are several ways to design a singleton. The
important concept is that there can only ever be one object of the singleton
class' type:

class TGlobalConnectionData
{
private:
TStream* SomeStream;

TGlobalConnectionData(); // hidden, singleton constructor

static TGlobalConnectionData* get_obj();

public:
~TGlobalConnectionData();

static TStream* get_Stream();
static void set_Stream(TStream* Value);
};

TGlobalConnectionData::TGlobalConnectionData() : SomeStream(NULL)
{
}

TGlobalConnectionData::~TGlobalConnectionData()
{
delete SomeStream;
}

TGlobalConnectionData* TGlobalConnectionData::get_obj()
{
static TGlobalConnectionData theData;
return &theData;
}

TStream* TGlobalConnectionData::get_Stream()
{
return get_obj()->SomeStream;
}

void TGlobalConnectionData::set_Stream(TStream* Value)
{
TGlobalConnectionData* obj = get_obj();
delete obj->SomeStream;
obj->SomeStream = Value;
}

// main
TStream* stream = new TMemoryStream;
TGlobalConnectionData::set_Stream(stream);

// somewhere in code
TStream* stream = TGlobalConnectionData::get_Stream();
stream->Position = 0;
// etc

Again, from what you've describe it appears to me that you don't *need* a
global but that it would make your life a lot easier. Instead of a global
you could design a class (much like the singleton above) that is a vessel
for your commonly used data. This object is managed by your main form and
passed to all other forms or classes that need its data.

class TMyAppConnectionData
{
// ...
};

// Main form .h
class TMainForm : public TForm
{
// ...

private:
TMyAppConnectionData MyAppConnectionData;

// ...
};

// construct another form that expects this object as an argument
TSubForm* subform = new TSubForm(this, &MyAppConnectionData);

// That subform constructs an object that expects the connection data as
// an argument
TMyConnectionClass mcc(&MyAppConnectionData);

If there are any concepts here that you are interested in but need
clarification on please post again.

HTH,
- Clayton


Thank you for this info... I understand most of it. What I'm having

difficulties with is:

I have the following TForm classes generated by the *IDE*:
(frmMain, frmLogon, frmUsers, ...)
I have the following classes that I made from scratch:
(CADOManager, CSQLManager, CUsers, CLocale, CCaptions, ...)

frmMain has these important public members:
AnsiString m_strADOCon // a string for the ado connection
int m_iLang // the language of the app
std::map<int, AnsiString> m_mapLoc // a map for localization
std::map<int, AnsiString> m_mapSQL // a map for SQL files

When the application starts, I initialize the members in frmMain.
Then I call frmLogon->ShowModal();
-------
Q1: How can I or should I say please suggest how to pass or access the
members of frmMain without frmLogon including frmMain?
e.g. frmMain->m_strADOCon;
-------

When the user inputs his/her username and password, I must connect to
the SQL server database and verify this info. My classes CADOManager and
CSQLManager do just this. But the way I designed them is that their
constructors need pointers to m_strADOCon, m_mapLoc, and m_mapSQL from
frmMain. It would look something like this everywhere in the code... messy.

#include "_class_ADOManager.h"
#include "_class_SQLManager.h"
#include "_class_User.h"

CADOManager *padoMan = new
CADOManager( &m_strADOCon, &m_mapLoc, &m_mapSQL );

// ... Do things with padoCon

CUser *pUser = new CUser( &m_strADOCon, &m_mapLoc, &m_mapSQL );

// ... Do things with pUser
frmUsers->ShowModal(); // Need to pass the vars again!!!

delete pUser;

// ... Do things with padoCon

delete padoCon;
-------
Q2: Is there any suggestion about how to design my classes in order to
not always have to pass these arguments everytime I create a new
CADOManager or CSQLManager object?
-------

Maybe the answer you gave me is ok but I don't understand the TSubForm?
Back to top
Clayton Arends
Guest





PostPosted: Mon Apr 23, 2007 6:25 am    Post subject: Re: static members in derived classes Reply with quote

Quote:
Thank you for this info...

I am glad to help. However, please trim previous responses before posting a
reply. It is a guideline of this newsgroup.

Quote:
frmMain has these important public members:
AnsiString m_strADOCon // a string for the ado connection
int m_iLang // the language of the app
std::map<int, AnsiString> m_mapLoc // a map for localization
std::map<int, AnsiString> m_mapSQL // a map for SQL files

First, I am going to show a non-global suggestion. For simplicity of this
post I will use a struct with no constructor or destructor. If you require
greater encapsulation feel free to do so (the same concepts would apply).

// This is declared in some globally included header file like
// MyDataTypes.h, ExtTypes.h, etc
struct TConnectionData
{
AnsiString m_strADOCon;
int m_iLang;
std::map<int, AnsiString> m_mapLoc;
std::map<int, AnsiString> m_mapSQL;
};

// main form
class TfrmMain : public TForm
{
//...
private:
TConnectionData ConnectionData;
//...
};

Quote:
When the application starts, I initialize the members in frmMain.
Then I call frmLogon->ShowModal();

I would normally modify the constructor but since you appear to be
auto-creating forms then you can add a setter method to TfrmLogon:

class TfrmLogon : public TForm
{
//...
private:
TConnectionData* ConnectionData;

public:
void SetConnectionData(TConnectionData* AConnectionData);
};

void TfrmLogon::SetConnectionData(TConnectionData* AConnectionData)
{
ConnectionData = AConnectionData;
}

// in the main form
frmLogon->SetConnection(&ConnectionData);
frmLogon->ShowModal();

Then, TfrmLogon can access ConnectionData without needing to know about
TfrmMain.

Quote:
CADOManager *padoMan = new
CADOManager( &m_strADOCon, &m_mapLoc, &m_mapSQL );

If you alter CADOManager to accept a single argument of type
TConnectionData* then you would have the one variable to pass.

However, from what I've seen of your design it might be easiest if you
create a TGlobalConnectionData singleton class instead. Put this class in a
unit (like GlobalConnectionData.cpp and .h) that is included by only the CPP
files that need the data (like _class_ADOManager.cpp, _class_User.cpp, the
main form, etc). Your main form will initialize the singleton's data and
the other classes can easily access this data. Here is an example (again,
encapsulate to your heart's desire)

class TGlobalConnectionData
{
private:
TGlobalConnectionData(); // hidden, singleton constructor

public:
AnsiString m_strADOCon;
int m_iLang;
std::map<int, AnsiString> m_mapLoc;
std::map<int, AnsiString> m_mapSQL;

~TGlobalConnectionData();

static TGlobalConnectionData* get_obj();
};

TGlobalConnectionData* TGlobalConnectionData::get_obj()
{
static TGlobalConnectionData theData;
return &theData;
}

// Somewhere where you will use the global data
TGlobalConnectionData* globalData = TGlobalConnectionData::get_obj();

SomeFunc(globalData->m_iLang);
// etc

Keep in mind. If you ever decide to support multiple connections,
especially if the program is multi-threaded, the global variable approach
becomes obsolete. You must then use an approach (like the first approach of
this post) where a data vessel is passed around.

Quote:
Maybe the answer you gave me is ok but I don't understand the TSubForm?

This is probably because you allow the IDE to add any new form you create to
the "Auto-create Forms" list. You can find this list in the project options
dialog. In my apps I only auto-create the main form and any data modules.
All other forms are not auto-created. In this way I am free to modify the
form constructors to my liking. In fact, the first thing I do when I create
a new form is to delete the global variable that the IDE creates for the
form.

- Clayton
Back to top
Stephane Baillargeon
Guest





PostPosted: Mon Apr 23, 2007 8:10 am    Post subject: Re: static members in derived classes Reply with quote

Quote:
I am glad to help. However, please trim previous responses before posting a
reply. It is a guideline of this newsgroup.

Sorry for the replies. A few years back I was told to include the
original post but not the entire posts. Thank you for pointing this out
to me.

Quote:
Keep in mind. If you ever decide to support multiple connections,
especially if the program is multi-threaded, the global variable approach
becomes obsolete. You must then use an approach (like the first approach of
this post) where a data vessel is passed around.

The class I created CADOManager manages multiple connection
simultaneously so I have no worries there. Connection object to connect
to the database are created dynamically without passing any info.

Quote:
This is probably because you allow the IDE to add any new form you create to
the "Auto-create Forms" list. You can find this list in the project options
dialog. In my apps I only auto-create the main form and any data modules.
All other forms are not auto-created. In this way I am free to modify the
form constructors to my liking. In fact, the first thing I do when I create
a new form is to delete the global variable that the IDE creates for the
form.

I would like to have a bit more clarification on this subject. I would
indeed like to control where and when my forms are created.

Thank you very much for the extensive help you are providing me with.
Back to top
Clayton Arends
Guest





PostPosted: Mon Apr 23, 2007 8:00 pm    Post subject: Re: static members in derived classes Reply with quote

Quote:
I would like to have a bit more clarification on this subject. I would
indeed like to control where and when my forms are created.

First you will need to edit an option in the IDE that controls form
auto-creation. In BCB this setting is at "Tools | Environment Options" tab
"Designer". In BDS this setting is at "Tools | Options" then "Environment
Options | VCL Designer". The check box titled "Auto create forms & data
modules" needs to be unchecked.

At this point what happens when you create a new project is the main form is
the only form that is auto-created. Whenever you add a new form it will no
longer get auto-created on startup. To see and edit the auto-created forms
head to "Project | Options" then navigate to the "Forms" page. There are
two lists: "Auto-create forms" and "Available forms". Use the interface to
control which forms get auto-created. If you have datamodules those need to
be placed in the list before any forms.

For forms that aren't auto-created you need to construct them like you would
any other class. You can either use the default auto-variables that the IDE
adds (not my preferred method) or remove the auto-variables from the code
files and only use stack variables when working the forms:

TfrmLogon* logon = new TfrmLogon(this);
logon->ShowModal();
logon->Release(); // or delete logon; I prefer Release()

I believe this is all you need to know about auto-creation. However, there
is one more concept I would be remiss not sharing. RAII is something that
every C++ programmer needs to know about. If you are not familiar with it
it stands for "Resource Acquisition is Initialization" and basically it is
mostly used as an exception-safe memory and resource management tool. There
exists a generic RAII class for managing object pointers (std::auto_ptr).
This is a templatized class that you "#include <memory>" to access.

Since I believe forms should be destroyed using Release() and not delete I
created and use the following RAII:

class auto_TForm
{
private:
TForm* fForm;

public:
inline auto_TForm(TForm* AForm) : fForm(AForm) {}
inline ~auto_TForm() { fForm->Release(); }
};

The code that I showed above would be rewritten like this:

TfrmLogon* logon = new TfrmLogon(this);
auto_TForm auto_logon(logon);
logon->ShowModal();

HTH,
- Clayton
Back to top
Stephane Baillargeon
Guest





PostPosted: Wed Apr 25, 2007 4:47 am    Post subject: Re: static members in derived classes Reply with quote

Thank you for your guidance, very appreciated.
Back to top
Display posts from previous:   
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> C++ Builder (Language C++) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.