//Main.cpp
/*
ProgEx30 - Customary Project Organization Of A C++ Project
Before continuing with our development of a String Class which we started on
in ProgEx29, lets examine how C++ projects are organized in terms of source
and header files.
The basic idea is to put equates, function/class prototypes/declarations, and
user defined types in a header file. These things don't require storage and
are used by the compiler at compile time.
Code and variables, that is, the implementations of ordinary functions or
class member functions go in *.cpp or *.c files. These entities require
storage at run time.
Its typical to make a seperate *.h / *.cpp file set for each class. Since we
have the beginnings of a String Class we can place the String Class
Declaration in a file named Strings.h Note the plural since one of the
Standard C Runtime Libraries is named string.h. It looks like this...
class String
{
public:
String(); //Uninitialized Constructor - Does nothing
String& operator=(char*); //Operator= for assigning char* to String
char* lpStr(void); //Returns Address of String data Member
~String(); //String Destructor
private:
char* pStrBuffer; //This char* member holds the address of Str buffer
};
The String.cpp file contains the implementations of the above String members.
In terms of syntax within that file the scope resolution operator is used to
identify the function as being a member of the class in question - here our
String Class. For example, our lpStr() member will look like so...
char* String::lpStr()
{
return this->pStrBuffer;
}
First comes the return value of the function which is pointer to char. Then
comes the name of a class which the compiler should have in its symbol table
by this time. And seperating the name of the class and the name of the member
is the scope resolution operator '::'. At this time you should examine the
String.h and String.cpp files to see the pattern.
Typically a program will have a file that contains the program's entry point
function which is Main.cpp in this example. If any instances of any classes
are going to be instantiated in Main.cpp the header for the class needs to be
included there. In this example we create an instance of our String Class in
Main.cpp so that header is included there.
In whatever programming environment you are using you no doubt have some kind
of class browser/file viewer associated with it. When you create the project
you need to include all the source and header files you need. Typically you
can do this several ways but the easiest is usually to place the source and
header files in the project directory then right click in the project viewer
to bring up a context sensitive menu to add various files. In this project
you'll have a Main.cpp, a Strings.cpp and a Strings.h
The way C/C++ compilers work is somewhat different from the PowerBASIC
compiler in that the compiler first creates *.obj files from the *.cpp files.
At that point the program's instructions have been converted to binary, but
the linker needs to run to finally resolve the various addresses and
references necessary to produce the executable. If there are any syntax
errors in the program the compiler will catch them, and the linker will not
even run. If the program compiles OK but the linker can't resolve some
reference then you'll get a linker error. These are oftentimes more troubling
than compilation errors.
*/
#include <stdio.h>
#include "Strings.h"
int main(void)
{
String s1;
printf("\n&s1 = %u\n",&s1);
s1="Fred";
printf("s1.lpStr() = %s\n",s1.lpStr());
printf("s1.lpStr() = %u\n",s1.lpStr());
getchar();
return 0;
}
/*
Entering Uninitialized Constructor!
&this->pStrBuffer = 2293584
this->pStrBuffer = 0
Leaving Uninitialized Constructor!
&s1 = 2293584
Entering String& operator=(const char* pStr)
pStr = Fred
pStrBuffer = 4079448
this = 2293584
Leaving String& operator=(const char* pStr)
s1.lpStr() = Fred
s1.lpStr() = 4079448
*/
//Strings.h
/*
Class Declaration For String Class
*/
class String
{
public:
String(); //Uninitialized Constructor - Does nothing
String& operator=(char*); //Operator= for assigning literal or char* to String
char* lpStr(void); //Returns Address of String data Member >> this->lpStr()
~String(); //String Destructor
private:
char* pStrBuffer; //This char* member holds the address of an allocated String
};
//String.cpp
/*
Implementation Of String Class
*/
#include <stdio.h>
#include <string.h>
#include "Strings.h"
/*
*****************************************************************************
This is an uninitialized String Constructor. It is called when a declaration
of a String such as this occurs...
String s1;
All it does assign NULL to the pStrBuffer member.
*****************************************************************************
*/
String::String()
{
printf("Entering Uninitialized Constructor!\n");
pStrBuffer=NULL;
printf(" &this->pStrBuffer = %u\n",(unsigned int)&this->pStrBuffer);
printf(" this->pStrBuffer = %u\n",(unsigned int)this->pStrBuffer);
printf("Leaving Uninitialized Constructor!\n");
}
/*
******************************************************************************
This overloaded operator= is called when a String is assigned a char*. In
other words, something like this...
String s1;
s1="Hello, World!"
or
char* pStr="Hello, World!";
s1=pStr;
The 1st thing the function does is check to see if this->pStrBuffer isn't
NULL. If it isn't then the String is holding some previous String the user
doesn't want anymore. In that case delete [] is called on the String. In any
case a new buffer is then allocated of a length equal to the length of the
char* pStr passed in plus an extra byte for the null terminator. Then the
passed in parameter string is copied to the newly acquired buffer. While this
points to a String, *this is a String, and now since the char* has been
transfered to the String's storage, we simply return *this.
******************************************************************************
*/
String& String::operator=(char* pStr)
{
printf("\nEntering String& operator=(const char* pStr)\n");
printf(" pStr = %s\n",pStr);
if(this->pStrBuffer)
delete pStrBuffer;
pStrBuffer=new char[strlen(pStr)+1];
printf(" pStrBuffer = %u\n",pStrBuffer);
strcpy(pStrBuffer,pStr);
printf(" this = %u\n",this);
printf("Leaving String& operator=(const char* pStr)\n\n");
return *this;
}
/*
*****************************************************************************
This function simply returns the pStrBuffer private char* address of the
String. It is useful for example in outputing the string to a file or
display with the printf function.
*****************************************************************************
*/
char* String::lpStr()
{
return this->pStrBuffer;
}
/*
*****************************************************************************
This is the String Destructor. It is called when a String is being
destroyed. In such cases it is considered good practice to set any class
buffers to 0 so the same address does't accidentally get deleted twice.
Calling delete on a NULL pointer is OK; calling it twice on a non null
pointer isn't OK, because there may be something else at that address.
*****************************************************************************
*/
String::~String() //String Destructor
{
delete pStrBuffer;
pStrBuffer=NULL;
}