Discussion:
Basic Form and Pointer question
(too old to reply)
w***@remov.yahoo.com
2007-06-04 22:55:08 UTC
Permalink
This is really a basic question, my only excuse is I am learning C++ and
Borland Builder at the same time.

I have an App that has two forms, in the main form ( Form1) I have declared
a pointer to a struct.

I can use that point fine within the main form, but I can not access it in
the second form.

I know it is a simple error, but digging thru the books I can not seems to
find the solution.

Form1.cpp

//---------------------------------------------------------------------------
#include <vcl\vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include "Unit2.h"
//---------------------------------------------------------------------------
#pragma resource "*.dfm"
TForm1 *Form1;

data *bb = new data; // data is a structure declared in unit2.h

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
bb->a = 24;
bb->b = 36; // This works Fine,
no problems
Label1->Caption = bb->a;
Label2->Caption = bb->b;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
delete bb;
}
//-------------


Form2.cpp


//---------------------------------------------------------------------------
#include <vcl\vcl.h>
#pragma hdrstop

#include "Unit3.h"
#include "Unit1.h"
#include "Unit2.h"
//---------------------------------------------------------------------------
#pragma resource "*.dfm"
TForm3 *Form3;
//---------------------------------------------------------------------------
__fastcall TForm3::TForm3(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm3::FormActivate(TObject *Sender)
{
Label1->Caption = Form1->bb->a; // This is my problem line,
Compiler says
//"bb is not a member of TForm1"
}
//

I know I have just done a declartion wrong somewhere, but am not having any
luck figuring it out tonight. Just a pointer in the right direction would be
great.


Thank you in Advance...

Michael
Remy Lebeau (TeamB)
2007-06-04 23:36:25 UTC
Permalink
Post by w***@remov.yahoo.com
data *bb = new data; // data is a structure declared in unit2.h
You are declaring a global variable that is proviate to within
Form1.cpp. The only way Form2.cpp will be able to know about it, let
alone access it, is to declare a 'extern' statement in Form2.cpp, ie:

extern data *bb;
Post by w***@remov.yahoo.com
void __fastcall TForm1::FormDestroy(TObject *Sender)
Do not use the OnDestroy event in C++. It is a Delphi idiom that can
introduce illegal behavior in C++ as it can be triggered after the
form's destructor. Use the actual destructor instead.
Post by w***@remov.yahoo.com
Label1->Caption = Form1->bb->a; // This is my problem line,
Compiler says
//"bb is not a member of TForm1"
That is because 'bb' really is not a member of the TForm1 class. So
you cannot access it via a pointer to a TForm1 object.


Gambit
Remy Lebeau (TeamB)
2007-06-04 23:37:26 UTC
Permalink
Post by Remy Lebeau (TeamB)
Do not use the OnDestroy event in C++. It is a Delphi idiom
that can introduce illegal behavior in C++ as it can be triggered
after the form's destructor. Use the actual destructor instead.
Likewise with the OnCreate event - it can be triggered before the
form's constructor, which is also illegal in C++.


Gambit
w***@remov.yahoo.com
2007-06-05 10:42:56 UTC
Permalink
<snip>
Post by Remy Lebeau (TeamB)
Post by w***@remov.yahoo.com
data *bb = new data; // data is a structure declared in unit2.h
You are declaring a global variable that is proviate to within
Form1.cpp. The only way Form2.cpp will be able to know about it, let
extern data *bb;
Post by w***@remov.yahoo.com
void __fastcall TForm1::FormDestroy(TObject *Sender)
Do not use the OnDestroy event in C++. It is a Delphi idiom that can
introduce illegal behavior in C++ as it can be triggered after the
form's destructor. Use the actual destructor instead.
Post by w***@remov.yahoo.com
Label1->Caption = Form1->bb->a; // This is my problem
line,
Post by w***@remov.yahoo.com
Compiler says
//"bb is not a member of TForm1"
That is because 'bb' really is not a member of the TForm1 class. So
you cannot access it via a pointer to a TForm1 object.
Gambit
Thanks Gambit

Reading up on the extern now. I knew it was simple. Thanks for the
explaination about onCreate, and onDestroy, I've run into some of that
behavior in onCreate already, moved the code elsewhere, but now know why it
did not work....

Thanks again

Michael
Chris Uzdavinis (TeamB)
2007-06-05 13:53:49 UTC
Permalink
Post by w***@remov.yahoo.com
I have an App that has two forms, in the main form ( Form1) I have
declared a pointer to a struct.
Ok...
Post by w***@remov.yahoo.com
TForm1 *Form1;
data *bb = new data; // data is a structure declared in unit2.h
Note that bb is a GLOBAL variable, not a member of the form. As such,
you should NOT be deleting it when Form1 is destroyed. (It's possible
that form1 is created and destroyed more than once, which will have
disastrous consequences on bb.)
Post by w***@remov.yahoo.com
Label1->Caption = bb->a;
Label2->Caption = bb->b;
Notice that you're accessing a global variable, not a member of
Form1. Had you fully qualified bb like this:

Label1->Caption = this->bb->a;

You would have received a compile-time error, because bb is not a
member.
Post by w***@remov.yahoo.com
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
delete bb;
}
Very, very dangerous.

A better approach would be to make bb not be a pointer, but be an
object. If it were simply declared like this

data bb;

Then the runtime environment would construct it automatically at
startup and destroy it at shutdown.

In the header, you'd do this:

extern data bb;

Then in the cpp file, you'd do this:

data bb;
Post by w***@remov.yahoo.com
void __fastcall TForm3::FormActivate(TObject *Sender)
{
Label1->Caption = Form1->bb->a; // This is my problem line,
Compiler says
//"bb is not a member of TForm1"
}
This is treating bb as if it were a member of Form1. You should drop
the "Form1->" qualification.
Post by w***@remov.yahoo.com
I know I have just done a declartion wrong somewhere, but am not
having any luck figuring it out tonight. Just a pointer in the right
direction would be great.
It's more than a declaration problem. You should be very careful
about lifetime management of the object. I suggest not having a
global pointer like that. If it must be a pointer, store it in an
auto_ptr instead. That'll ensure automatic cleanup. Or better yet,
don't allocate it and declare it as a variable.

Then you need to consider if you really want a global variable. It's
exposed and vulnerable to change by literally any code, and thus not
easy to debug if it gets into a bad state. Globals are discouraged
as they tend to break encapsulation and cause code maintenance
problems.
--
Chris (TeamB);
JD
2007-06-05 14:25:35 UTC
Permalink
Post by Chris Uzdavinis (TeamB)
[...] in the main form (Form1) I have declared a pointer to
a struct.
Note that bb is a GLOBAL variable, not a member of the
form. As such, you should NOT be deleting it when Form1
is destroyed. (It's possible that form1 is created and
destroyed more than once,
Oops. You missed that Form1 is the TApplication::MainForm
so when it closes (is destroyed), the application exits.

~ JD
Chris Uzdavinis (TeamB)
2007-06-05 14:41:42 UTC
Permalink
Post by JD
Oops. You missed that Form1 is the TApplication::MainForm
so when it closes (is destroyed), the application exits.
Actually, I didn't miss that. To me, if code is wrong it doesn't
matter if some external condition hides the bug or somehow prevents it
from actually running. I'm very sensitive against the attitude that
something clearly wrong is "ok" because of such-and-such. The problem
with that attitude is that it tolerates bad code and actually helps
extend the lifetime of bugs by providing a friendly, fertile feeding
ground for them to exist. The *attitude* of "no bugs, ever" is far
more hostile to bugs, and does not extend anything to help them exist
longer.

Maintaining brittle, buggy code is extremely difficult. When "correct
behavior" depends on external attributes to make it work, you are in
grave danger whenever you edit anything. Move things around, or run
the code in a different order, and suddenly everything falls apart.

For example, how hard is it to change what the main form is? Would
you bet your life, company, or personal income on the fact that
Form1's OnDestroy is only called once? I use this only as an example,
though, because the idea extends to all other areas as well.
--
Chris (TeamB);
JD
2007-06-05 15:50:55 UTC
Permalink
Post by Chris Uzdavinis (TeamB)
Post by JD
Oops. You missed that Form1 is the TApplication::MainForm
Actually, I didn't miss that.
For the record, I agree with your original solution and the
remainder of this post 100%.

I was just pointing out the fact that you said:

(It's possible that form1 is created and destroyed more
than once,

and from the given information, that is inaccurate.

~ JD
Chris Uzdavinis (TeamB)
2007-06-05 16:02:44 UTC
Permalink
Post by JD
(It's possible that form1 is created and destroyed more
than once,
and from the given information, that is inaccurate.
We must have different ideas on what it means for something to be
"possible".

If all it takes is for someone to do this:

void somefunction()
{
TForm1 * f = new TForm1(parent);
delete f;
}

The nasty problem is that Form1 does not need to change whatsoever to
exhibit the problem. Only the code that makes use of it must change.
Therefore it's possible, without even touching the Form1 code.

If it's invalid to instantiate or destroy an object, it usually
indicates the code is wrong, because creating and destroying objects
is one of the cornerstones of C++. (If it really shouldn't be allowed
to be instantiated or destroyed, it should either be documented or
implemented such that it prevents invalid use of it.)
--
Chris (TeamB);
w***@remov.yahoo.com
2007-06-05 21:04:51 UTC
Permalink
On 5-Jun-2007, Chris Uzdavinis (TeamB) <***@uzdavinis.com> wrote:
<snip>
Post by Chris Uzdavinis (TeamB)
It's more than a declaration problem. You should be very careful
about lifetime management of the object. I suggest not having a
global pointer like that. If it must be a pointer, store it in an
auto_ptr instead. That'll ensure automatic cleanup. Or better yet,
don't allocate it and declare it as a variable.
Then you need to consider if you really want a global variable. It's
exposed and vulnerable to change by literally any code, and thus not
easy to debug if it gets into a bad state. Globals are discouraged
as they tend to break encapsulation and cause code maintenance
problems.
--
Chris (TeamB);
Ok
That brings me to a bigger question then. How to move information around the
program. I will have a number of Forms that the user will enter the
information on. I need a "central" placeholder to hold the "current"
information, so that when a Form is opened, it's behavior is determined by
items contained in this "central placeholder" structure.

I am new to all of this, so apologize for the "basic" questions, but am self
teaching this by reason of need. Am slowly grasping the concept of OOP and
encapsulation.

My APP is a cabinet design software. I am thinking of having a series of
classes for seperate types of cabinets. Lets say for the moment a
BaseCabinetClass, and a WallCabinetClass.

My basic thought is the MainForm being my area for collecting and "storing"
parts of the information as the program aquires it thru a series of
secondary forms. And as the user works thru the forms, an instance of the
class gets filled with it's data. Once the information for the individial
cabinet is aquired, we move onto the next cabinet, and another instance of
the class. This might happen for 1 occurance or 20 per room.

With my limited understanding of encapsulation, should I make each class
responsible for aquiring it's needs through the class's methods? (It's own
forms ect)

Can you suggest any books that would be good for me to read on the OOP and
encapsulation thought process?

Thanks in Advance

Michael
Ed Mulroy
2007-06-05 21:41:59 UTC
Permalink
Try using the visual items like forms and the controls they contain only as
visual items. Declare a normal, non-VCL class to contain the actual data.
Make the data private (best) or protected (not best). Declare an instance
or instances of that class and let the controls in forms call functions in
that class to update the data and other functions in that class to access
the saved data.

In other words, separate the user interface from the work that the program
does. In the long run that results in a program which is easier to maintain
and which is easier to port across platforms.

. Ed
Post by w***@remov.yahoo.com
Ok
That brings me to a bigger question then. How to move information around the
program. I will have a number of Forms that the user will enter the
information on. I need a "central" placeholder to hold the "current"
information, so that when a Form is opened, it's behavior is determined by
items contained in this "central placeholder" structure.
I am new to all of this, so apologize for the "basic" questions, but
am self teaching this by reason of need. Am slowly grasping the
concept of OOP and encapsulation.
My APP is a cabinet design software. I am thinking of having a
series of classes for seperate types of cabinets. Lets say for
the moment a BaseCabinetClass, and a WallCabinetClass.
My basic thought is the MainForm being my area for collecting
and "storing" parts of the information as the program aquires
it thru a series of secondary forms. And as the user works thru
the forms, an instance of the class gets filled with it's data. Once
the information for the individial cabinet is aquired, we move onto
the next cabinet, and another instance of the class. This might
happen for 1 occurance or 20 per room.
With my limited understanding of encapsulation, should I make
each class responsible for aquiring it's needs through the class's
methods? (It's own forms ect)
Can you suggest any books that would be good for me to read on
the OOP and encapsulation thought process?
Chris Uzdavinis (TeamB)
2007-06-06 04:28:39 UTC
Permalink
Post by w***@remov.yahoo.com
That brings me to a bigger question then. How to move information around the
program.
The typical way is to have an object (or struct) that contains the
values and you pass it around to whoever needs it. Alternately (more
easily but less cleanly) you make it available in a "well known" place
so that code that needs it can find it. Global variables take this
second approach, but a slightly better design is to use a singleton
pattern.

However, I've run into many troubles with singleton objects, and have
decided that I like a "global context" object to store things that
would otherwise be a singleton or globals. The reason for this is
when you have a singleton, you really can have only one single
instance. The problem arises when the code that USES the singleton
may wish to be seperated, so that one instance of X uses one
singleton, and another instance of X would like its "own" singleton.
I've run into this primarily in testing situation, where I want more
than one instance of an object in the same application, even though in
a production setting I'd only have one.
Post by w***@remov.yahoo.com
I will have a number of Forms that the user will enter the
information on. I need a "central" placeholder to hold the "current"
information, so that when a Form is opened, it's behavior is
determined by items contained in this "central placeholder"
structure.
Like Ed said, it's good to store your data in non visual objects. The
VCL is a nice easy-to-use library for building an interface, but if
you start intertwining your data and program logic in your forms, you
are in for a very difficult time. For one, suppose you would like to
run on a text-mode unix terminal... if your logic and data are
seperated from your presentation layer, you can simply rewrite a new
interface in text that doesn't use the VCL at all, and build that
without having to redo the rest of your application.

Maybe that's overkill for a small, simple application, but it's still
good to think about layers and seperation.
Post by w***@remov.yahoo.com
I am new to all of this, so apologize for the "basic" questions, but am self
teaching this by reason of need. Am slowly grasping the concept of OOP and
encapsulation.
No problem. Welcome!
Post by w***@remov.yahoo.com
My basic thought is the MainForm being my area for collecting and
"storing" parts of the information as the program aquires it thru a
series of secondary forms. And as the user works thru the forms, an
instance of the class gets filled with it's data. Once the
information for the individial cabinet is aquired, we move onto the
next cabinet, and another instance of the class. This might happen
for 1 occurance or 20 per room.
You can have non-visual objects that have the same kind of
relaionship, but that are cleanly kept off to the side.
Post by w***@remov.yahoo.com
With my limited understanding of encapsulation, should I make each
class responsible for aquiring it's needs through the class's
methods? (It's own forms ect)
Yes.

However, encapsulation is best thought of as "protecting my details
from everyone else". Think of a car. The driver simply uses the car
without having to look at anything under the hood. In that regard, it
is well encapsulated, and rather self-contained.
Post by w***@remov.yahoo.com
Can you suggest any books that would be good for me to read on the
OOP and encapsulation thought process?
That's hard. There are a *lot* of books, and it depends on what
you're most intersted in. The ACCU website (www.accu.org) has a lot
of book reviews, which can help steer you way from "bad" books, and
toward "good" ones.
--
Chris (TeamB);
Continue reading on narkive:
Loading...