 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Stephane Baillargeon Guest
|
Posted: Sat Apr 21, 2007 7:35 pm Post subject: static members in derived classes |
|
|
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
|
Posted: Sun Apr 22, 2007 6:22 am Post subject: Re: static members in derived classes |
|
|
| Quote: | Is it good practice to use a global class to derive from for storing
public vars (static)?
|
It's definitely a practice . 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
|
Posted: Sun Apr 22, 2007 8:10 am Post subject: Re: static members in derived classes |
|
|
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.
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
|
Posted: Sun Apr 22, 2007 8:10 am Post subject: Re: static members in derived classes |
|
|
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 . 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
|
Posted: Sun Apr 22, 2007 6:56 pm Post subject: Re: static members in derived classes |
|
|
| 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
|
Posted: Sun Apr 22, 2007 7:52 pm Post subject: Re: static members in derived classes |
|
|
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
|
Posted: Sun Apr 22, 2007 8:17 pm Post subject: Re: static members in derived classes |
|
|
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
|
Posted: Sun Apr 22, 2007 10:24 pm Post subject: Re: static members in derived classes |
|
|
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
|
Posted: Mon Apr 23, 2007 6:25 am Post subject: Re: static members in derived classes |
|
|
| 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
|
Posted: Mon Apr 23, 2007 8:10 am Post subject: Re: static members in derived classes |
|
|
| 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
|
Posted: Mon Apr 23, 2007 8:00 pm Post subject: Re: static members in derived classes |
|
|
| 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
|
Posted: Wed Apr 25, 2007 4:47 am Post subject: Re: static members in derived classes |
|
|
| Thank you for your guidance, very appreciated. |
|
| Back to top |
|
 |
|
|
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
|
|