Next, we extended the struct construct (inherited from C) as we created a new circle data type.
//File: circle_struct.cpp
#include <iostream>
#include <string>
using namespace std;
const double PI=3.14159;
struct circle
{
//Member data fields
double m_rad;
double m_x, m_y;
string m_name;
//Member functions -- DON'T HAVE THESE IN C
circle() //Constructor
{
m_x=0.0;
m_y=0.0;
m_rad=1.0;
m_name="Unit Circle";
}
//Overloaded constructor
circle (double xcoord, double ycoord,
double radius, string name)
{
m_x=xcoord;
m_y=ycoord;
m_rad=radius;
m_name=name;
}
~circle() //Destructor
{
}
//NOTE: Constructors and destructors are called
automatically.
// You're not permitted to call them yourself.
// Also, note that they have no return type.
//Accessor Methods
double circumference() const
//The use of const here prevents
//changing or updating the member data.
{
return (m_rad*2.0*PI);
}
double radius() const
{
return m_rad;
}
string name() const
{
return m_name;
}
void center (double & xcoord, double
& ycoord) const
{
xcoord=m_x;
ycoord=m_y;
}
};
int main()
{
circle c, d(1.0, 3.0, 5.0, "Test Circle");
double x, y;
c.center(x,y);
cout << c.name() << " is located at (" << x
<< "," << y << ")" << endl;
cout << "Its radius is: " << c.radius() <<
endl;
cout << "Its circumference is: " <<
c.circumference() << endl;
cout << endl;
d.center(x,y);
cout << d.name() << " is located at (" << x
<< "," << y << ")" << endl;
cout << "Its radius is: " << d.radius() <<
endl;
cout << "Its circumference is: " <<
d.circumference() << endl;
cout << endl;
return 0;
}
The use of struct in the above example is legal C++, but somewhat unorthodox. More typically, it is the class structure which is employed. If we go back and change struct to class, however, the program will not compile. The reason has to do with the distinction between public fields available for general use and private fields ( not accessible outside the class definition). In fact, this is the essential difference between the C++ struct and the C++ class. Namely, all fields in a struct are by default public, while all fields in a class are by default private (inaccessible).
We have to come to grips with the software engineering principles which apply here. Typically, it is desirable to protect the class data from outside use and possible corruption. So, in general, we would have:
//File: circle_class.cpp
#include <iostream>
#include <string>
using namespace std;
const double PI=3.14159;
class circle
{
private: //These fields are
accessible ONLY within the circle class.
//Member data fields
double m_rad;
double m_x, m_y;
string m_name;
public: //These methods are
accessible anywhere within the scope of
//the circle class
//Member functions -- DON'T HAVE THESE IN C
circle() //Constructor
{
m_x=0.0;
m_y=0.0;
m_rad=1.0;
m_name="Unit Circle";
}
//Overloaded constructor
circle (double xcoord, double ycoord,
double radius, string name)
{
m_x=xcoord;
m_y=ycoord;
m_rad=radius;
m_name=name;
}
~circle() //Destructor
{
}
//NOTE: Constructors and destructors are called
automatically.
// You're not permitted to call them yourself.
// Also, note that they have no return type.
//Accessor Methods
double circumference() const
//The use of const here prevents
//changing or updating the member data.
{
return (m_rad*2.0*PI);
}
double radius() const
{
return m_rad;
}
string name() const
{
return m_name;
}
void center (double & xcoord, double
& ycoord) const
{
xcoord=m_x;
ycoord=m_y;
}
};
int main()
{
circle c, d(1.0, 3.0, 5.0, "Test Circle");
double x, y;
c.center(x,y);
cout << c.name() << " is located at (" << x
<< "," << y << ")" << endl;
cout << "Its radius is: " << c.radius() <<
endl;
cout << "Its circumference is: " <<
c.circumference() << endl;
cout << endl;
d.center(x,y);
cout << d.name() << " is located at (" << x
<< "," << y << ")" << endl;
cout << "Its radius is: " << d.radius() <<
endl;
cout << "Its circumference is: " <<
d.circumference() << endl;
cout << endl;
return 0;
}
While we're on the topic of software engineering principles, encapsulation and information hiding suggests that we should separate our new data type into an interface (or header file) and an implementation (*.cpp) part. This is done in the usual way, with definitions and prototypes in the header or .h file and the actual function definitions in the .cpp file. You can access and download the files by clicking on the following:
If you are using Visual C++, you now have three choices:
Place all three files in your project. Compile and link.
Place the circle.h file in your INCLUDE directory, say C:\MyInclude. Then, add this directory to the list of Include directories as follows: Go to Tools on the menu line and click Options. Select the Directories tab. Click on Show directories for : Include files. At the bottom of the list, type in your directory, C:\MyInclude. Place circle.cpp and circle_driver.cpp in your project. Compile and link.
Put circle.h in your INCLUDE directory as described in 2. Compile circle.cpp into the library as follows. Go to File on the menu line and click New. On the Project tab, choose Win32 Static Library. Give it a name, say mylib. Now add circle.cpp, the implementation file which goes with circle.h, to your project. Now select Build and your library is created. Exit the Visual Studio environment and copy the mylib.lib file (in the Debug folder of your project) to a directory of your choosing, say C:\MyLibrary. Finally, change the name of the file mylib.lib to Ole32.lib. Go back to Visual Studio and add the directory C:\MyLibrary to the list of default library directories. (This is done in exactly the same way that you added your include directory, except, of course, you want to click on Show directories for: Library files.) If you drag your directory to the top of the list, it will be the first one searched. Your Ole32.lib file will take precedence over the system library of the same name. Now, when you want to use circles, you do not need to add circle.cpp to your project. It will find the object code for circle.cpp in the library file Ole32.lib. (By the way, O(bject) L(inking) and E(mbedding), get it?)
Note that in 1 and 2, the implementation file, circle.cpp, must be compiled each time you use the circle type. By adding it to the library, it is precompiled and is merely linked to your code for execution.
Recall the lab exercise from 3/5 when you constructed an array of records and performed input and output. My version is given in array.cpp. If the records have many fields, this could prove to be very inefficient. The following rewrite incorporates an array of pointers to records instead. Then each record is constructed as needed.
//File: arrayAlt.cpp
#include <fstream>
#include <string>
using namespace std;
struct student_record
{
string name;
int
age;
double gpa;
};
int main()
{
//Using array of pointers to records could be more space efficient
student_record * student[30];
ifstream fin("students_in.txt");
ofstream fout("students_out.txt");
int k;
for (k=0; !fin.eof(); k++)
{
student[k] = new
student_record; //dynamic memory allocation
fin >>
student[k]->name
>> student[k]->age
>> student[k]->gpa;
}
for(int j=0; j<k; j++)
{
fout <<
(*student[j]).name << '\t'
<< (*student[j]).age << '\t'
<< (*student[j]).gpa
<< endl;
delete student[j];
//releases memory back to heap
}
return 0;
}
Applied to students_in.txt, identical results are obtained for both versions.
Lab Exercise. Write a employee class with 4 data members, name, ssNum, age, and salary. Add member function raise() to the class. Its prototype looks like:
void raise (const double & x) //gives x % raise to employee
Write a driver program that incorporates the Employee class. If you get frustrated, you can check my solution to the challenge, employee.cpp and driver.cpp
Lab Exercise. Write a fraction class that incorporates the four operations add(), subtract(), multiply(), and divide(). Add accessor functions getNumerator() and getDenominator(). Use the testFraction driver program to test your work.