Imagine you create a CCircle class as follows:
Header File: circle.h
|
#pragma once
namespace FlatShapes
{
public __gc class CCircle
{
protected:
double radius;
public:
CCircle(void);
CCircle(double r);
double Diameter();
double Circumference();
double Area();
public:
__property void set_Radius(double r);
__property double get_Radius();
};
}
|
The class can be implemented as follows:
Source File: circle.cpp
|
#include "StdAfx.h"
#include ".\circle.h"
#using <mscorlib.dll>
namespace FlatShapes
{
CCircle::CCircle(void)
: radius(0.00)
{
}
CCircle::CCircle(double r)
: radius(r)
{
}
void CCircle::set_Radius(double r)
{
radius = (r < 0) ? 0 : r;
}
double CCircle::get_Radius()
{
return (radius < 0) ? 0 : radius;
}
double CCircle::Diameter()
{
return Radius * 2;
}
double CCircle::Circumference()
{
return 3.14159 * radius * 2;
}
double CCircle::Area()
{
return radius * radius * 3.14159;
}
}
|
This class can be tested as follows:
// This is the main project file for VC++ application project
// generated using an Application Wizard.
#include "stdafx.h"
#using <mscorlib.dll>
#include ".\circle.h"
using namespace System;
using namespace FlatShapes;
void ShowCharacteristics(CCircle *round);
int _tmain()
{
// TODO: Please replace the sample code below with your own.
CCircle *circle = new CCircle(35.75);
ShowCharacteristics(circle);
Console::WriteLine(S"");
return 0;
}
void ShowCharacteristics(CCircle *round)
{
Console::WriteLine(S"Circle Characteristics");
Console::WriteLine(S"Radius: {0}", __box(round->Radius));
Console::WriteLine(S"Diameter: {0}", __box(round->Diameter()));
Console::WriteLine(S"Circumference: {0}", __box(round->Circumference()));
Console::WriteLine(S"Area: {0}", __box(round->Area()));
}
This would produce:
Circle Characteristics
Radius: 35.75
Diameter: 71.5
Circumference: 224.623685
Area: 4015.148369375
Press any key to continue
In an inheritance scenario, a base class is configured to
provide its children with the basic foundation the derived classes need.
Although a derived class can implement a new behavior not available on the parent
class, sometimes the derived class will need a customized implementation of a
behavior that has already been configured with its parent. That is what happens
for example when inheriting a sphere from a circle class. Both have a
characteristic called Area() but the area is calculated differently on each
shape. For example, you can derive a CSphere class as follows:
Header File: Sphere.h
|
#pragma once
#include ".\circle.h"
namespace Volumes
{
class CSphere : public FlatShapes::CCircle
{
public:
CSphere(void);
CSphere(double x);
double Area();
double Volume();
};
}
|
This object can be implemented as follows:
Source File: Sphere.cpp
|
#include "StdAfx.h"
#include ".\sphere.h"
#using <mscorlib.dll>
namespace Volumes
{
CSphere::CSphere(void)
{
}
CSphere::CSphere(double n)
: FlatShapes::CCircle(n)
{
}
double CSphere::Area()
{
return 4 * 3.14 * radius * radius;
}
double CSphere::Volume()
{
return 4 * 3.14 * radius * radius * radius / 3;
}
}
|
In the same way, you can derive as many classes as necessary
that share the same base class. For example, you can create a CCylinder class based on the above CCircle as follows:
Header File: Cylinder.h
|
#pragma once
namespace Volumes
{
class CCylinder : public FlatShapes::TCircle
{
public:
CCylinder(void);
CCylinder(double r, double h);
double BaseArea() const;
double LateralArea() const;
double Area() const;
double Volume() const;
__property void set_Height(double h);
__property double get_Height();
protected:
double height;
};
}
#endif
|
This object can be implemented as follows:
Source File: Cylinder.h
|
#include "StdAfx.h"
#include ".\cylinder.h"
#using <mscorlib.dll>
namespace Volumes
{
CCylinder::CCylinder(void)
{
}
CCylinder::CCylinder(double x, double y)
: FlatShapes::CCircle(x), height(y)
{
}
void CCylinder::set_Height(double h)
{
height = (h < 0) ? 0 : h;
}
double CCylinder::get_Height()
{
return height;
}
double CCylinder::BaseArea()
{
return FlatShapes::CCircle::Area();
}
double CCylinder::LateralArea()
{
double circ = radius * 2 * 3.14;
return circ * height;
}
double CCylinder::Area()
{
double base = FlatShapes::CCircle::Area();
double lateral = LateralArea();
return (2 * base) + lateral;
}
double CCylinder::Volume()
{
double base = FlatShapes::CCircle::Area();
return base * height;
}
}
|
The above three classes could be tested with the following:
// This is the main project file for VC++ application project
// generated using an Application Wizard.
#include "stdafx.h"
#using <mscorlib.dll>
#include ".\circle.h"
#include ".\sphere.h"
#include ".\cylinder.h"
using namespace System;
using namespace Volumes;
using namespace FlatShapes;
void ShowCharacteristics(CCircle *round);
void ShowCharacteristics(CSphere *Sph);
void ShowCharacteristics(CCylinder *C);
int _tmain()
{
// TODO: Please replace the sample code below with your own.
CCircle *circle = new CCircle(35.75);
CSphere *sphere = new CSphere(22.64);
CCylinder *cylinder = new CCylinder(18.94, 14.58);
ShowCharacteristics(circle);
Console::WriteLine(S"");
ShowCharacteristics(sphere);
Console::WriteLine(S"");
ShowCharacteristics(cylinder);
Console::WriteLine(S"");
return 0;
}
void ShowCharacteristics(CCircle *round)
{
Console::WriteLine(S"Circle Characteristics");
Console::WriteLine(S"Radius: {0}", __box(round->Radius));
Console::WriteLine(S"Diameter: {0}", __box(round->Diameter()));
Console::WriteLine(S"Circumference: {0}", __box(round->Circumference()));
Console::WriteLine(S"Area: {0}", __box(round->Area()));
}
void ShowCharacteristics(CSphere *Sph)
{
Console::WriteLine(S"Sphere Characteristics");
Console::WriteLine(S"Radius: {0}", __box(Sph->Radius));
Console::WriteLine(S"Diameter: {0}", __box(Sph->Diameter()));
Console::WriteLine(S"Circumference: {0}", __box(Sph->Circumference()));
Console::WriteLine(S"Area: {0}", __box(Sph->Area()));
Console::WriteLine(S"Volume: {0}", __box(Sph->Volume()));
}
void ShowCharacteristics(CCylinder *cyl)
{
Console::WriteLine(S"Cylinder Characteristics");
Console::WriteLine(S"Radius: {0}", __box(cyl->Radius));
Console::WriteLine(S"Height: {0}", __box(cyl->Height));
Console::WriteLine(S"Base Area: {0}", __box(cyl->BaseArea()));
Console::WriteLine(S"Lateral Area: {0}", __box(cyl->LateralArea()));
Console::WriteLine(S"Area: {0}", __box(cyl->Area()));
Console::WriteLine(S"Volume: {0}", __box(cyl->Volume()));
}
|
This would produce:
Circle Characteristics
Radius: 35.75
Diameter: 71.5
Circumference: 224.623685
Area: 4015.148369375
Sphere Characteristics
Radius: 22.64
Diameter: 45.28
Circumference: 142.2511952
Area: 6437.874176
Volume: 48584.4904482133
Cylinder Characteristics
Radius: 18.94
Height: 14.58
Base Area: 1126.962474524
Lateral Area: 1734.191856
Area: 3988.116805048
Volume: 16431.1128785599
Press any key to continue
When studying inheritance, we learned that there is a
special bond between an inherited class and its parent. Not only does the child
object have access to the public members of a class but also the child,
based on this relationship, has direct access to the members of the protected
section(s) of the parent. The program above shows us that, in order to access a
class’ members, we can just declare an instance of the class and use either
the member access operator “->”.
If there is such a good relationship between a class and its children, is it
possible to access a member of a child object using an instance of the parent?
We will declare only an instance of the parent class, CCircle, and try to access
its child using such a variable:
// This is the main project file for VC++ application project
// generated using an Application Wizard.
#include "stdafx.h"
#using <mscorlib.dll>
#include ".\circle.h"
#include ".\sphere.h"
#include ".\cylinder.h"
using namespace System;
using namespace Volumes;
using namespace FlatShapes;
void ShowCharacteristics(CCircle *round);
int _tmain()
{
// TODO: Please replace the sample code below with your own.
CSphere *sphere = new CSphere(22.64);
CCylinder *cylinder = new CCylinder(12.94, 14.58);
// Declare a CCircle variable and initialize it
// with a CSphere variable
CCircle *circle = sphere;
ShowCharacteristics(circle);
Console::WriteLine(S"");
// Initialize a CCircle with a CCylinder variable
circle = cylinder;
ShowCharacteristics(circle);
Console::WriteLine(S"");
return 0;
}
void ShowCharacteristics(CCircle *round)
{
Console::WriteLine(S"Circle Characteristics");
Console::WriteLine(S"Radius: {0}", __box(round->Radius));
Console::WriteLine(S"Diameter: {0}", __box(round->Diameter()));
Console::WriteLine(S"Circumference: {0}", __box(round->Circumference()));
Console::WriteLine(S"Area: {0}", __box(round->Area()));
}
|
This would produce:
Circle Characteristics
Radius: 22.64
Diameter: 45.28
Circumference: 142.2511952
Area: 1610.283529664
Circle Characteristics
Radius: 12.94
Diameter: 25.88
Circumference: 81.3043492
Area: 526.039139324
Press any key to continue
|
Notice that, although the variable circle has been
initialized with variables sphere and cylinder, only the characteristics of the circle
are displayed.
A virtual method is a member function that makes sure that, in an
inheritance scenario, the right method is accessed regardless of the expression
that calls the object. The last three classes we have used have a function
called Area() each. Let's consider the member
function Area() that is present in all three classes. If we need
an instance of the parent class to call the right member function, in this case
Area, we must precede its declaration in the parent class as the virtual
keyword as follows:
Header File: Circle.h
|
#pragma once
namespace FlatShapes
{
public __gc class CCircle
{
protected:
double radius;
public:
CCircle(void);
CCircle(double r);
double Diameter();
double Circumference();
virtual double Area();
public:
__property void set_Radius(double r);
__property double get_Radius();
};
}
|
With just this change, the program would produce the
following:
Without virtual Area()
|
With virtual Area()
|
Circle Characteristics
Radius: 22.64
Diameter: 45.28
Circumference: 142.2511952
Area: 1610.283529664
Circle Characteristics
Radius: 12.94
Diameter: 25.88
Circumference: 81.3043492
Area: 526.039139324
Press any key to continue
|
Circle Characteristics
Radius: 22.64
Diameter: 45.28
Circumference: 142.2511952
Area: 6437.874176
Circle Characteristics
Radius: 12.94
Diameter: 25.88
Circumference: 81.3043492
Area: 2236.895734648
Press any key to continue
|
Notice that, this time, even though an instance of the
CCircle class made the call, the characteristics of the assigned variables
display. Any other member function that is implement on the parent as well
as the inherited class can be made virtual in the base class also. Once again, the right functions, as far as their having been
assigned to the base class instance goes, are called. The ability for the right
method to be called when a base and its children share the same method is called
polymorphism. Even though you can use polymorphism to help the compiler identify
which function is being called, if you want to access the method of the base
class, you can qualify it with the :: operator.
As you can see, a method with the same name can be declared in both a base and a
derived classes. This allows a derived class to define its custom definition of
the function member. Because the derived class “re-writes” the same
function, the derived class is said to override the method. A member function
can be overridden only if it is virtual. A function can be virtual
only if carries the same name, the same return type, and the same type(s) of
argument(s) if any.
In inheritance, if you are inside of a method of a derived
class, you may be trying to use a method that is defined in both the base and
the derived class. To indicate that you want to call the method of the base
class, you can precede its call with the __super keyword. An example would be
__super::CallingMethodFromBase();
An abstract class is one whose role is only meant to
lay a foundation for other classes that would need a common behavior or similar
characteristics. Therefore, an abstract class is used only as a base class for
inheritance. In C++, to make class become abstract, at least one of its member
functions must be declared as a “pure” virtual method. Only a virtual method can be made “pure”. The
syntax of declaring a pure virtual method is:
virtual ReturnType MethodName() = 0;
|
The virtual keyword is required to make sure that a
(any) child of this class can implement this method as it judges it. The ReturnType
is the data type that the function will return. The MethodName is an
appropriate name for the function. The = 0 is required. It lets the compiler
know that this function is a pure virtual function. Here is an example of a C++
abstract class.
struct TTent
{
virtual int WhatIsTheCapacity() = 0;
virtual double TentArea() = 0;
virtual double TentVolume() = 0;
};
|
In Managed C++, to declare a class as abstract, type the __abstract
keyword to the left of its name when creating it. Here is an example:
Header File: quadrilateral.h |
#pragma once
__abstract __gc class CQuadrilateral
{
};
|
If you are creating an assembly class and want to
specify the class' access level, you can type the __abstract
keyword before or after the access level keyword. The following two are
equivalent:
Header File: quadrilateral.h |
|
#pragma once
public __abstract __gc class CQuadrilateral
{
};
|
#pragma once
__abstract public __gc class CQuadrilateral
{
};
|
You don't have to define any method for the abstract
class but you can. As with C++, you can create either only regular
methods, only pure virtual methods, or a mix. Here is an example with two pure virtual methods for the above
class:
Header File: quadrilateral.h |
#pragma once
__abstract public __gc class CQuadrilateral
{
public:
virtual double Perimeter(void) = 0;
virtual double Area(void) = 0;
};
|
After creating an abstract class, you can derive a
class from it. The first rule you must observe is that, a derived class
must implement any pure virtual method that the parent class has. In other
words, if you derive a class from the above abstract class, you must
define your versions of the above Perimeter() and the above Area()
methods;
otherwise, the derived class also would be considered abstract. As an
alternative, you can declare a method simply as virtual, implement it in
the abstract class, and then use it in the derived classes.
Here is an example of a class called CSquare that is
based on the above CQuadrilateral class:
Header File: Square.h |
#pragma once
#include "quadrilateral.h"
public __gc class CSquare : public CQuadrilateral
{
protected:
double side;
public:
CSquare(void);
virtual double Perimeter(void);
virtual double Area(void);
public:
__property void set_Side(double s);
__property double get_Side(void);
};
|
Source Code: Square.cpp |
#include "StdAfx.h"
#include ".\square.h"
#using <mscorlib.dll>
CSquare::CSquare(void)
: side(0)
{
}
void CSquare::set_Side(double s)
{
side = s;
}
double CSquare::get_Side(void)
{
return side;
}
double CSquare::Perimeter(void)
{
return side * 4;
}
double CSquare::Area(void)
{
return side * side;
}
|
If you try declaring a variable from the above
CQuadrilateral class, you would receive an error:
// This is the main project file for VC++ application project
// generated using an Application Wizard.
#include "stdafx.h"
#using <mscorlib.dll>
using namespace System;
public __abstract __gc class CQuadrilateral
{
};
int _tmain()
{
// TODO: Please replace the sample code below with your own.
CQuadrilateral *Quad = __gc new CQuadrilateral;
Console::WriteLine(S"");
return 0;
}
|
The error produced is Error C3622: 'CQuadrilateral': a
class declared as '__abstract' cannot be instantiated.
On the other hand, since the above CSquare class is
not abstract, you can use it in your program as you see fit. Here is an
example:
// This is the main project file for VC++ application project
// generated using an Application Wizard.
#include "stdafx.h"
#include ".\square.h"
#using <mscorlib.dll>
using namespace System;
int _tmain()
{
// TODO: Please replace the sample code below with your own.
CSquare *Sqr = __gc new CSquare;
Sqr->Side = 25.65;
Console::WriteLine(S"Square Characteristics");
Console::WriteLine(S"Side: {0}", Sqr->Side.ToString());
Console::WriteLine(S"Perimeter: {0}", Sqr->Perimeter().ToString());
Console::WriteLine(S"Area: {0}", Sqr->Area().ToString());
Console::WriteLine(S"");
return 0;
}
|
This would produce:
Square Characteristics
Side: 25.65
Perimeter: 102.6
Area: 657.9225
Press any key to continue
In order to use inheritance, one class considered the
parent must have been created and made available to other classes. As done
with abstract classes, sometimes you may want to create a class that would be
used only to provide a foundation for other classes. Such a class would
never be instantiated. Such a class is called an interface.
To create an interface class, you can use the
__interface keyword. When creating an interface class, type the __interface
keyword on the left of the name of the class. By (good) habit, it is usual
to start the name of an interface class with I to indicate the nature of
the class. Here is an example that creates an interface class:
Header File: quadrilateral.h |
__interface IQuadrilateral
{
double Perimeter();
double Area();
};
|
As mentioned already, the __interface
keyword
indicates that the class is abstract. Therefore, you don't have to
indicate that its methods are virtual. Otherwise, if you want, you can
still type the virtual keyword to the left of the method:
__interface IQuadrilateral
{
double Perimeter();
virtual double Area();
};
Also, since an interface class is only made of pure
virtual methods, you don't have to indicate this by typing = 0; to their
left, but you can if you want to:
__interface IQuadrilateral
{
double Perimeter();
virtual double Area() = 0;
};
In case you wonder why we declared only methods in the
class, an interface class can have only methods, no member variables.
While the __interface concept is primarily a re-enforcer of the notice
of abstract classes, it adds some rules to set the difference with a regular C++
abstract class:
- An interface class can inherit from only another interface class,
not any regular class
- An interface class can have only methods, no member variables; the
methods are already pure virtual
- The methods of an interface class can only be public; private or
protected methods are not allowed
- Constructors and destructors are not allowed in an interface class;
when you derive a class from it, you can create the necessary
constructors and destructor
- Static methods are not allowed in an interface class
The CQuadrilateral class we used earlier can be made
an interface with just two changes while keeping everything else:
Header File: quadrilateral.h |
#pragma once
__abstract public __gc class IQuadrilateral
{
public:
virtual double Perimeter(void) = 0;
virtual double Area(void) = 0;
};
|
Header File: Square.h |
#pragma once
#include "quadrilateral.h"
public __gc class CSquare : public IQuadrilateral
{
protected:
double side;
public:
CSquare(void);
virtual double Perimeter(void);
virtual double Area(void);
public:
__property void set_Side(double s);
__property double get_Side(void);
};
|
The most common reasons of creating an abstract class is to
lay a foundation that derived classes can benefit from. Even if you don't make a
class abstract, as done in the examples of the previous lesson, a user can still
derive another class from it and use it as necessary. If you want to prevent
classes from being derived from your class, you can seal it. A sealed class is
one that other classes cannot inherit from. In other words, the class is used
"as is".
When creating a class, to mark it as sealed, type the __sealed
keyword on its left when creating the class. Here is an example:
#pragma once
public __sealed __gc class CTriangle
{
};
When creating a sealed class, you should provide
functionality as complete as possible since other classes cannot derive from
that class. Here is an example of a sealed class created and tested:
Header File: triangle.h |
#pragma once
public __sealed __gc class CTriangle
{
public:
CTriangle(void);
double Area(void);
private:
double base;
double height;
public:
__property void set_Base(double b);
__property double get_Base(void);
__property void set_Height(double h);
__property double get_Height(void);
};
|
|
Source File: triangle.cpp |
#include "StdAfx.h"
#include ".\triangle.h"
#using <mscorlib.dll>
CTriangle::CTriangle(void)
: base(0), height(0)
{
}
void CTriangle::set_Base(double b)
{
if( b < 0 )
base = 0;
else
base = b;
}
double CTriangle::get_Base(void)
{
return base;
}
void CTriangle::set_Height(double h)
{
if( h < 0)
height = 0;
else
height = h;
}
double CTriangle::get_Height(void)
{
return height;
}
double CTriangle::Area(void)
{
return base * height / 2;
}
|
The class could be tested as follows:
// This is the main project file for VC++ application project
// generated using an Application Wizard.
#include "stdafx.h"
#include ".\triangle.h"
#using <mscorlib.dll>
using namespace System;
CTriangle *CreateTriangle();
void DescribeTriangle(CTriangle* T);
int _tmain()
{
// TODO: Please replace the sample code below with your own.
CTriangle __gc *general = __gc new CTriangle;
general = CreateTriangle();
DescribeTriangle(general);
Console::WriteLine(S"");
return 0;
}
CTriangle *CreateTriangle()
{
CTriangle __gc *tri = __gc new CTriangle;
tri->Base = 32.65;
tri->Height = 20.48;
return tri;
}
void DescribeTriangle(CTriangle* triangle)
{
Console::WriteLine(S"Triangle Characteristics");
Console::WriteLine(S"Base: {0}", triangle->Base.ToString());
Console::WriteLine(S"Height: {0}", triangle->Height.ToString());
Console::WriteLine(S"Area: {0}", triangle->Area().ToString());
}
This would produce:
Triangle Characteristics
Base: 32.65
Height: 20.48
Area: 334.336
Press any key to continue
|