المساعد الشخصي الرقمي

مشاهدة النسخة كاملة : الفرق بين لغة c و لغة ++C



amro_ka
21-05-2002, 09:55 PM
تقريبا الفروق ليست كبيرة ببساطة الفروق هى انه فى لغة c++
يوجد functions اكتر وطريقة الكتابة قد تختلف لبعض الأوامر ليس أكثر
يعنى مثلا فى السى تستخدم أمر printfللطباعة على الشاشة و فى الليبرارى stdio.h
اما فى c++ تستخدم أمر cout and the library is iostream.h

والبرمجة من كومبيلر لأخر لاتختلف يعنى لا يختلف الكومبيلر الفيجوال عن البورلاند فى طريقة البرمجة أو الأوامر الأساسية ولكن قد يختلف طريقة الكتابة والليبرارى ليس أكثر وان كنت تبرمج على البورلاند تستطيع ان تفتح هيلب الفيجوال وترى طريقة كتابة الأمر اللذى تريده ولن تجد فرق كبير

بإختصار ان كنت تستطيع البرمجة على السى فإن حاولت البرمجة على السى ++ سوف تنجح ولكن يتطلب النجاح بعض المجهود

Black_Horse82
23-05-2002, 06:55 AM
عذرا أخي ....أظنك قد أخطأت....

هناك فروق كبيرة جدا جدا ......

أنا ألآن أبرمج برمجة متقدمة في ال C (وقبل ذلك برمجة إلى مستوى متوسط في ال C++ :! ?: )....والإختلافات كما ذكرت تشمل طريقة الكتابة نوعا ما أضافة إلى بعض الخصائص المتقدمة....مثل ....
inheritecne......metamorphics......tamplete....classes...etc

amro_ka
23-05-2002, 10:14 PM
الرسالة الأصلية كتبت بواسطة Black_Horse82
عذرا أخي ....أظنك قد أخطأت....

هناك فروق كبيرة جدا جدا ......

أنا ألآن أبرمج برمجة متقدمة في ال C (وقبل ذلك برمجة إلى مستوى متوسط في ال C++ :! ?: )....والإختلافات كما ذكرت تشمل طريقة الكتابة نوعا ما أضافة إلى بعض الخصائص المتقدمة....مثل ....
inheritecne......metamorphics......tamplete....classes...etc
انت لو فاهم ال c جيدا ستكن ال c++ بالنسبة لك ليست صعبة
بعض الخصائص الجيدة والمختلفة عن الc مثل ما ذكرت بعضهم قد تكون غريبة بالنسبة لك ولكن ان حاولت دراستها لن تكون بالشىء الصعب
وكل ما يمكن عمله بالc يمكن عمله أيضا بالc++

Black_Horse82
24-05-2002, 08:14 AM
أنا أختلف معك يا أخي......

هذا صحيح اذا كنت فقط تريد ان تتعلم كيف تقوم ببناء برنامج صغير جدا ...
مثل أن تبرمج برنامج لطباعة رسالة صغيرة او برنامج لحساب عملية رياضية صغيرة ....

على العموم إذا كنت تريد فعلا أن تفهم الفرق بين C و C++ هو في الجملتين التاليتين :

البرمجة بالC هي برمجة بالخوارزميات العادية : يعني "" خوارزميات الأعلى-الى-الأسفل " أو با لإنجليزي "top-down algorithm"

أما البرمجة بالC++ هي برمجة بالكائنات(1) "object-oriented Programming" وهذا إختلاف كبير جدا جدا ....


----------------------
(1) ومن لغات البرمجة بالكائنات ال Java !!!!

amro_ka
26-05-2002, 11:38 PM
هذا أيضا فرق مهم الذى ذكرته ولكن إن تأملت فى هذا الموضوع ستجد أنه ليس بالصعب ان كنت فاهم ال c جيد ستجد نفسك تكتب c ايضا فى c++ لا تجعل الشباب يفهمو ان ال c , & ++c لغات برمجة مختلفة تماما أنا فى رأيى ‘نها برمجة متقدمة بال c ليس أكثر ولكن بالمناسبة للتوضيح للقراء ال object orineted ليست فى c++ فقط ولكن فى لغات برمجة كثيرة مثل vhdl و java

ehab
27-05-2002, 12:34 AM
مرحبا شباب

زيادة على كلامكم شباب فأنت تستطيع استخدام السي++ ببرمجة الخوارزميات لكن طريقةكتابة الاوامر مختلفة نوعا ما و فقط
ففي النهاية اي برنامج سي تسطيع كتابته بالسي ++ (طبعا مع تغير طريقة كتابة الاوامر ) لكن العكس ليس صحيح فأنت لا تسطيع كتابة اي برنامج سي بالسي ++

amro_ka
27-05-2002, 10:27 PM
انا معك يا ايهاب كلامك 100 100 صحيح:cool:

Black_Horse82
27-05-2002, 10:49 PM
السلام عليكم....

يا شباب .....الكلام هذا صحيح اذا كنتم تبرمجون برامج صغيرة أما بالنسبة إذا كان برمجة برامج كبيرة ( مشاريع projects ) فإن code ال C سوف يختلف اختلافا جذريا ....عن Code ال C++

وللأخ ehab هذا صحيح أنه بامكانك البرمجة بالخوارزميات العادية في ال C
و أيضا في ال Java

ولكن يفضل كثيرا ( وهذا ما يحصل في معظم الأحيان إن لم يكن كلها) ان تكون البرمجة في ال C++ برمجة بالكائنات (OOP ) object-oriented Programming

amro_ka
27-05-2002, 11:01 PM
وما المشكلة الأن هذا فقط للتوضيح ليس أكثر سؤال لك يا بلاك هورس
أيهما أسهل وأسرع فى التعلم للغة ال ++C
1- مبرمج له خبرة سابقة بال سى
2- مبرمج له خبرة سابقة بأى لغة أخرى

وهل توافقنى أن الكود الذى يكتب فى الC++ هو نفسه الكود الذى يكتب فى ال c ولكن مع بعض التغيير فى صيغة الأوامر بشكل عام؟

Black_Horse82
27-05-2002, 11:30 PM
الجواب :

أكيد مبرمج له خبرة في ال C

وحتى بالنسبة لشخص له خبرة سابقه في اي لغة ولكن الأفضل لمبرمج له خبرة سابقة بال سى ......

http://cplus.about.com/

شيك على الوصلتين هذي .....في مقالات رائعة تشرح الفرق ( زي ما قولنا الفرق في البرامج الكبيرة وي المعقدة)

http://users.abcs.com/gmagill/cpages/cpextras.html

http://www.radix.net/~universe/Polymorphism.htm

------------------------------------------------------------------------------------------------------------

C++, what are the extras?
On this page:
Classes
Differences in layout and methods
Comment lines
Functional notation for type-conversions
New declaration of functions without parameters
Prototyping
Standard input and output streams
Alternatives for #define
What is new for variables?
Declaring variables between statements
Easier use of structs and enums
Extended possibilities of const
Memory allocation with new and delete
Reference variables
Overloading
Remark: in these pages some knowledge of ANSI C is presumed.
Classes
Other than structs and unions (see Dynamic data structures in C, what is the advantage?) C++ also offers classes.
Classes offer many more possibilities than structs and unions. It is, for instance, possible to include functions in classes (in addition to just variables). Also, it is possible to specify for each class-item ('member' as it is called) if they can only be accessed by the members of the same class, or also by members of friend classes or all other classes.
Most importantly, classes provide an easy way to practice object-oriented programming. Classes are probably the most important addition to ANSI C.
Because classes are such an extended subject, there will be a page concentrating on classes available soon.
Differences in layout and methods
Comment lines
C++ offers a new way to add comments to your source code. In addition to the /* and */ symbols to use comments in C, C++ offers the // symbol. There is a difference between /*/*/ and //. In C, /* is used to start a comment, and */ to end it. This works if the comment takes up just one line, but also if it is using more. The C++ symbol // works from the point where it is used until the end of the line it is standing on.
Example:

C

/*Program: comment.c
Description: Example of multiple-line comments in C*/

int i; /* This is a single-line-comment */

C++
//Program: comment.cpp
//Description: Example of multiple-line comments in C++

int i; // This is a single-line comment

Functional notation for type-conversions
In C, it is possible to explicitly convert the type of a variable by the use of casts. For example, to assign the value of a double variable dvar to an integer variable ivar, you could do: ivar = (int) dvar;. This works for all types, including user defined types and pointers. So, this could also be done: ipvar = (int *) &dvar;, if ipvar is an int pointer.
C++ offers a new way to achieve this: via a functional notation. This means that instead of ivar = (int) dvar;, also ivar = int(dvar); could be used. This also works for all types and user defined types, but not for pointers. The code ipvar = int *(&dvar); would therefore not work.
This is a problem that can be solved by defining an additional type.
Example:

typedef int *intp;

int main()
{ int *ipvar;
double dvar;
ipvar = intp(&dvar);
}

New declaration of functions without parameters
As I presume known, in ANSI C, a function that takes no parameters has to be declared with the word void: int noparams(void);. In C++, the keyword void may be left away: int noparams();.
However, it still is required to use void to specify that a function has no return value.

Prototyping

I have already treated prototypes in Header files, what are they and what do you need them for?, where I stated that they should be used to tell the compiler of what types the functions are that are not yet defined. In ANSI C they can be left away, in C++ non-prototyped functions result in an error.

Standard input and output streams

Next to the standard functions printf and scanf, C++ offers standard input and output streams. There is a significant difference between streams and fuctions. Streams do not take parameters, but get there input by guiding it to the stream.
For example, instead of

printf("Integer: %d Char: %c String: %s", 5, 'A', "Last in Line");

you could use
cout << "Integer: " << 5 << " Char: " << 'A' << " String: " << "Last in
Line";

The operator cin works in a similar way:
scanf("%d", &ivar);

would become
cin >> &ivar;

Similar to the printf and scanf functions, the cin and cout streams offer many ways to determine how the output should look, or what input should be accepted. I did not list all the possible options for the functions and I will not list them for the streams either =).
Alternatives for #define

As mentioned in Header files, what are they and what do you need them for? the #define directive can be used for two purposes:

to replace certain bits of text in the source file by others;
to create "named marks", for example to show a certain header file is loaded.
The first of those is used for basically two reasons:

to give numeric constants a meaningful name.
Example: #define TRUE 1
to replace frequently returning source code by a function-like name, that can also take parameters. The source code replaced is called an inline function.
Example: #define calc(x,y) ((x + y) * (x / y))
C++ offers alternatives for both uses.
For the constants you can use const, for the inline functions inline. As const is already available in ANSI C I will not treat it here.
Besides, some special characteristics of C++ const are covered later at Extended possibilities of const.
The inline keyword is very simple in use: just put it in front of the declaration and definition of the function you want to make inline. So, instead of
#define calc(x,y) ((x + y) * (x / y))

use
inline int calc(int x, int y)
{ return (x + y) * (x / y);
}

Using inline makes inline functions more 'natural' than the #define directive, but has one problem: unlike #define, it is not possible to declare one inline function that works on different types of input values.
For example, with #define you could do:
ivar = calc(34, 16);

as well as
rvar = calc(3.67, 8.67);

However, with inline
rvar = calc(3.67, 8.67);

would not work, as the function only accepts integer parameters.
However, this problem can be solved by overloading, treated later at Overloading.
One note, the inline specification is just an advise to the compiler. That means that, in case the compiler finds the function too long or too complicated, inline will be ignored. In that case the function will be compiled as a normal function.

What is new for variables?
Declaring variables between statements
In ANSI C, local variables can only be declared before the first statement of the function is executed, i.e. only at the beginning of a function.
In C++, variables can be declared everywhere in the source code. Something that can be very handy but should not be forgotten, is that a variable delared in a 'control statement' (e.g. if, while, do, for) is local to that statement.
For example, in the code

int main()
{ int i = 6, j = 0;
for (int k = 0; k < j; k++)
printf("%d\n", k);
return 1;
}

the variable k only exists in the for loop it is declared in. So, any attempt to use k after that loop will result in a compiler error stating the veriable does not exist.
Easier use of structs and enums

If you declare a struct or enum in ANSI C, you have to place the appropriate keyword in front of every declaration that uses that struct or enum.
Example:

enum week
{ Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
};

int main()
{ enum week today;
enum week whichday(int day, int month, int year);
int daynum(enum week day);
today = whichday(9, 7, 1977);
printf("I was born on day %d of the week.\n", daynum(today));
return 1;
}

In C++, every occurance of enum in this example (except for the first of course) can be dropped.
This works similar for structs.
Extended possibilities of const

In ANSI C, a const variable, just like a normal one, always takes up memory. In C++, the compiler tries to replace the occurences of the const variable by the value that was assigned to it at initialization. I will now give a short list of the different ways const can be used:

int i; i is a normal variable
const int j = 3; j is a constant variable (*)
const int *k; k is a normal pointer to a constant variable
int *const l = &i; l is a constant pointer to a normal variable (*)
const int *const m = &j; m is a constant pointer to a constant variable (*)

In the cases marked with (*) a value must be assigned at initialization because that is simply not possible after initialization.
It is not possible to assign the address of a normal variable to a 'pointer to a constant variable'.
Memory allocation with new and delete

Instead of the malloc and free functions in ANSI C, C++ offers new and delete. new and delete are not functions, but real keywords.
List of possibilities:

int *i = new int; sizeof(int) bytes are allocated to i
int *j = new int[35]; 50 * sizeof(int) bytes are allocated to j
delete i; the memory allocated to i is released
delete j; the memory allocated to j is released

It speaks for itself that new also works for user-defined types, including structs, enums, unions and classes and that it can be used anywere in the program and not only at initialization.
Note: allocating memory for objects of a class must be done with new and delete to make the constructors and destructors (if available) be called.
Reference variables

Next to normal and pointer variables, C++ also offers reference variables.
Reference variables work just like the variable they refer to, but if one is passed to a function, only the reference is passed and not all the data in the variable itself. Especially for structs and class-objects this can save a lot of time and memory.
Example:

int main()
{ int i = 3, j = 5, k;
int &l = i;
void refmult(int &x, int &y, int &result);
refmult(i, j, k);
printf("%d\n", k);
l = 6;
printf("%d\n", i);
return 1;
}

void refmult(int &x, int &y, int &result)
{ result = x * y;
}

The output of this code is 15 and 6, and that is all I think is necessary to understand the way the reference variables work. =)
Note: reference variables have to be initialized when they are declared (either in the header or the body of a function).
Overloading
In C++, it is possible to make more functions with the same name but with different parameters, either in name or type.
The return type may also be different, but only if the parameters are too. Declaration and definition of the overloaded functions works similar to not-overloaded functions.
On to How to get more subjects treated on the pages.


Rutger van Bergen, rbergen@2support.nl

Back to The C Programmer's Main.

------------------------------------------------------------------------------------------------------------



------------------------------------------------------------------------------------------------------------


The Beauty and Power of C++

By Elliott Coates



If you believe that it's possible for a person to be transported as a beam of brilliant energy, as in Star Trek, then you should know that it is likely to be choreographed by highly polymorphic software systems. Polymorphism means many forms; it denotes a single thing having multiple forms. E unum pluribus, Out of one many. In the object-oriented software area, polymorphism means that a single thing is able to behave in many different, but related ways. Or that a single thing can elicit many different, but related behaviors. Or finally, that a single thing is able to accept many different, but related forms of data for processing.

Only through the workings of software which is extremely adaptable, and flexible can things like energy beam transfer occur and these workings are the kind of characteristics which highly polymorphic systems offer. Of course, there's no question that any software system de-materializing, and re-materializing people had better be extremely successful, robust, and reliable. Extremely high flexibility, and reliability are qualities that can be realized together in object-oriented software systems developed using C++.

One of the major ways many well designed C++ systems achieve these qualities is by making extensive and significant use of inheritance based polymorphism, along with strong type checking. Inheritance based polymorphism has been shown to be the most powerful of the many forms of polymorphism. Inheritance based polymorphism allows us to develop systems which are extremely adaptable, and flexible.

C++ inheritance based polymorphism allows us to develop systems which behave efficiently and effectively in a wide variety of circumstances, contexts, and conditions. Polymorphic systems are the most capable for effectively addressing a wide variety of application needs, and demands. C++ inheritance based polymorphic systems perform well in the face of a wide variety of input conditions, and output necessities.

Dynamic substitution is the mechanism which underlies much of the polymorphism found in many C++ systems. Dynamic substitution takes place within (horizontally), as well as across (vertically) the various levels of C++ systems having a layered architecture[1]. Dynamic substitution allows these systems to attain a high degree of flexibility. Flexible systems are adaptable to many different circumstances.

Visually the structures, and architectures of well designed C++ programs which are highly polymorphic are beautiful, and conceptually these programs are elegant. When you use the proper levels of abstraction, and make judicious use of inheritance based polymorphism in C++, it is an experience echoing Dijkstra's comment that well abstracted code is self-documenting. And that as you write the code, it is apparent that it is correct.[2]

With these things in mind we will explore the best ways to implement, and exercise inheritance based polymorphism using C++. C++ easily supports constructing amazing, mind bending, and highly polymorphic systems.



Working on Vehicles

Let's take a look at the two major classes we will use to illustrate a system with a high degree of polymorphism. They are the classes Vehicle and BodyDivision; a Vehicle is worked on by the BodyDivision:



class Vehicle

{

private: // visible only in Vehicle

bool exportStatus; // boolean flag

char vehicleType; // vehicle type

public: // visible to the world

bool isExport( )

{

return exportStatus;

};

char getVehicleType( )

{

return vehicleType;

};



Vehicle(char _vehicleType, bool _exportStatus) // contsructor [3]

{

exportStatus = _exportStatus;

vehicleType = _vehicleType;

};

};



class BodyDivision

{

private:

void Ship(Vehicle aVehicle)

{

// ship aVehicle

};

void Stripe(Vehicle aVehicle)

{

// stripe aVehicle

};

public:

void stripeOrShip(Vehicle aVehicle)

{

if (aVehicle.isExport( ) == true)

{

Stripe(aVehicle);

}

else

{

Ship(aVehicle);

}

};

};



The two might be used like this:

int main( )

{

Vehicle ourFerrari('S', true); // create 'S'portster for export

BodyDivision ourBodyDivision; // create a BodyDivision

ourBodyDivision.stripeOrShip(ourFerrari); // send ourFerrari to

// ourBodyDivision to

// stripe or ship it

return 0;

};

In the BodyDivision class, if a Vehicle is for export, the BodyDivision class has the function Stripe( ) to give the Vehicle stripes.



Dynamically Substituting Implementation

Using inheritance based polymorphism in C++, it is easy to change the specifics of how something is implemented[4] on the fly; it is possible to change how something is done dynamically during program execution. This is dynamic substitution of implementation.

In real life there isn't just one kind of vehicle, there are various kinds such as sports, off-road, and van vehicles. More than likely the striping process is somewhat different for each kind of vehicle. As things stand now BodyDivision would have to use an 'if' or 'switch' statement to deal with each kind of vehicle. Something like this fragment:

typeVehicle = ourFerrari.getVehicleType( );

switch (typeVehicle)

{

case 'O':

// do off-road vehicle stripes

break;

case 'S':

// do sports vehicle stripes

break;

case 'V':

// do van vehicle stripes

break;

// etc.

};




It would be inconvenient to have to specify the striping process for every kind of new vehicle introduced into a program. It also error prone because we might forget to add the appropriate striping operation in the switch construct when a new kind of vehicle is introduced.

To get around this we can implement (define) the striping procedure, Stripe( ), specific to each kind of vehicle inside the class code for each kind of vehicle; we will take Stripe( ) out of the BodyDivision class. When the BodyDivision works on each kind of vehicle it can ask the vehicle to stripe itself.

To make this work efficiently in C++, the various kinds of vehicle classes should be publicly inherited[5] (publicly derived) from a common vehicle class. The classes would be arranged in a hierarchy, much like an organizational chart with the common vehicle class where the president would be placed and the various derived classes where the vice-presidents would be located. The common class may be thought of as a parent class and the various kinds of vehicles which inherit from it may be thought of as child classes.

In this case let's call the common, parent class, Vehicle (ignoring the fact that we used the name for an earlier class). We will call the child subclasses derived from the Vehicle class: OffRoadVehicle, SportsVehicle, and VanVehicle.

As we proposed, the Stripe( ) function will be implemented inside each of the children vehicle types. Given such a scenario in C++, the common base class, in this case Vehicle, simply declares (announces) the operations, such as Stripe( ), common to all of the classes which inherit from it. It declares the common function, but leaves the implementation up to each child vehicle subclass.

When we simply declare a common function, such as Stripe( ) in the Vehicle class, we prepend the declaration with the word 'virtual' and append it with the pure virtual specifier '= 0'. "Pure" means that we are only declaring, not defining, the function in the Vehicle class declaration. This forces the child subclasses of Vehicle to define (implement) the function declaration themselves. Here is the new Vehicle class serving as the base class of our vehicle hierarchy of classes:



class Vehicle

{

public:

virtual bool isExport( ) = 0;

virtual void Stripe( ) = 0;

};




When a C++ class has at least one 'virtual' function appended with the pure virtual specifier '= 0' it is an abstract class. So we see that class Vehicle is an abstract class.

Here is one of the child types, a SportsVehicle subclass, inheriting from our new Vehicle class. Notice in the code how the SportsVehicle subclass inherits from Vehicle by placing a colon after its name followed by the Vehicle class name:

class SportsVehicle : public Vehicle

{

private:

bool is_export;

public:

virtual void Stripe( )

{

// specific striping code for SportsVehicles

};

virtual bool isExport( )

{

return is_export;

};

SportsVehicle(bool _is_export)

{

is_export = _is_export;

};

};




Next is an example of the BodyDivision class which is able to process the various subclasses of Vehicle. As we proposed earlier, the function Stripe( ) is no longer a member of the BodyDivision class. Because a subclass of the abstract Vehicle base class is now being passed into the BodyDivision class, not a Vehicle class itself, as before, an asterisks follows the name Vehicle when it is used in the BodyDivision class:



class BodyDivision

{

private:

void Ship(Vehicle* vehiclePointer)

{

// ...

};

public:

void stripeOrShip(Vehicle* vehiclePointer)

{

if (vehiclePointer->isExport( ) == true)

{

vehiclePointer->Stripe( );

}

else

{

Ship(vehiclePointer);

}

};

};




The arrow means that the variable 'vehiclePointer' activates the functions isExport( ), and Stripe( ).

With this new BodyDivision class, during run-time (dynamically) the implementation of striping can change, depending on which Vehicle child class is being processed by BodyDivision. In other words we can "dynamically substitute" one kind of implementation process, or procedure for another. There is no need to make changes to BodyDivision whenever it receives a new kind of Vehicle subclass for export.

Our program, or system is less subject to changes which might break it in various places. BodyDivision does not have to be modified to work on any new subclass of Vehicle; it is open to any new subclasses of Vehicle which may be introduced into the program any time in the future.[6] This is a significant way to use abstract base classes, and their subclasses to achieve polymorphism, and dynamic substitution in C++.



Objects and Pointers

Before going on, it should be explained that while the abstract Vehicle base class has various concrete subclasses - OffRoadVehicle, SportsVehicle, and VanVehicle - these concrete subclasses, in all but rare cases, must be instantiated into unique objects to be useful. Instantiation means that a copy of a class, or subclass is given the ability to operate its functions, or store its data by the operating system.

A class, or subclass may have zero, one, or any number of unique object instances, each having a different state from the others. For instance, the set of actual SportsVehicle objects in a program at any one time may be a set of different colored Mazda RX-7s, or a set combining various makes such as RX-7s, Triumph GTBs, Nissan 340Zs, etc.

While the children subclasses of the Vehicle classes may be instantiated into objects, it is not possible to instantiate the Vehicle class into an object. That is because when we modified the Vehicle class, we made it a C++ abstract base class (ABC). It is not possible to create (instantiate) objects of a C++ ABC. However, the good thing about C++ ABC's is that we may use them as gateways, or interfaces to elicit the behavior of their children subclasses.

This is done by using a pointer variable[7], which has the type of the ABC, to call (invoke) the specific behavior of each child subclass. The 'vehiclePointer' argument, in our new Vehicle class, is a pointer variable which has the type of the Vehicle ABC. It may contain the address of instantiated objects which are copies of any of the subclasses inherited from Vehicle: OffRoadVehicle, SportsVehicle, and VanVehicle.

Every C++ variable has a compile time (static) type and for 'vehiclePointer' its static type is the Vehicle class. If we were going to use 'vehiclePointer' for more than just passing data to functions, we could declare it like any other variable:

Vehicle* vehiclePointer; // declare a Vehicle type pointer

Since the various Vehicle subclasses all inherit from Vehicle, a pointer variable having a Vehicle class static type may contain the address of any object instantiated from any of these subclasses. Again, this is why Vehicle in the stripeOrShip() function, of the new BodyDivision class, has an asterisks after it.[8]



Dynamically Substituting Policy

Now let's gain more flexibility by dynamically changing, or substituting policy as necessary. With policy we are more concerned with what will be done not how it is done.

The following code fragment from the function stripeOrShip( ) in the new BodyDivision class, is an example of dynamically changing, or substituting, policy as necessary. The pointer interface to the Vehicle hierarchy, 'vehiclePointer', and the BodyDivision functions', Ship( ), and Stripe( ) are entities within the same horizontal layer of our program[9]:

if (vehiclePointer->isExport( ) == true)

{

vehiclePointer->Stripe( );

}

else

{

Ship(vehiclePointer);

}


Here the policy switch is made using the 'if' construct. We have dynamically chosen our policy by testing whether, or not an actual Vehicle subclass object (for example a given actual Mazda RX-7 object of the SportsVehicle subclass represented by vehiclePointer) is for export. In this code we are determining "what" should be done. If the subclass object will be exported, it is given stripes, else it is sent to Shipping.

Earlier we dynamically invoked a specific striping implementation (how something is done) for each Vehicle subclass class - OffRoadVehicle, SportsVehicle, or VanVehicle - using the pointer to the Vehicle base class 'vehiclePointer'. In the 'if' statement example, we have used the same pointer to determine policy, or "what" will be done, not implementation (how) it will be done.

While the abstract pointer, 'vehiclePointer', exists within the same horizontal layer as the functions of BodyDivision, the concrete functions the pointer calls generally exist in layers below (vertical to) to that of BodyDivision. The 'vehiclePointer' variable, may be used as an abstract, horizontal, interface to vertical, concrete objects.



Horizontal and Vertical System Pointers

Let us call abstract base class pointers to functions of policy, horizontal pointers. Abstract base class pointers to functions of implementation, let us call vertical pointers. Policy pointers are called horizontal pointers because, for systems with a layered architecture, policy generally changes within a horizontal layer. Implementation pointers are called vertical pointers because for systems with a layered architecture, implementation generally takes place in layers below the policy abstraction layer.

While, horizontal, and vertical pointers are two very useful ways of thinking about (conceiving) C++ pointers, in layered architecture systems, the C++ language standard does not define entities called horizontal pointers, or vertical pointers[10].

Here is a diagram of a C++ layered architecture system conceived with horizontal, and vertical system pointers. Click on the following link to see the diagram: Figure 1.

Because all of the individual horizontal, and vertical pointers of a C++ program may vary both the specific subclass, and specific object address within a subclass they point to, the topology, and operation of a running C++ program is highly variable, and dynamic. This allows the system to flex, and adapt well to a wide range of circumstances. Included in this adaptability is the ability of highly polymorphic systems to offer a wide variety of inputs, outputs and services.

The ability of an ABC pointer variable, like 'vehiclePointer', to vary the address of the subclass objects it points to during program execution is called dynamic substitution. Dynamic substitution allows the capabilities of a highly polymorphic system to change as needed. It also allows the system to be highly extensible. At any one instance the configuration of a highly polymorphic system is different from the next, sometimes radically so. It is almost as if some extremely polymorphic systems were intelligent, and alive with self will.

Highly polymorphic systems make extensive use of both horizontal, and vertical base class pointers. It is through wise system design, and use of both horizontal, and vertical pointers that highly flexible and adaptable software systems may be created. The topology, and structure of a highly polymorphic system varies instant by instant according to the ever changing configuration of its horizontal and vertical pointers.[11] These are the same kind of pointers as, 'vehiclePointer', used by the BodyDivision class example in Figure 1.

Of course, what is horizontal in one context is vertical in another, and vice versa. And it is no different for horizontal, and vertical pointers. The determination of what is a horizontal pointer versus what is a vertical pointer is relative to the level of the system being examined. In other words, it depends on the context. Also, it should be noted that, the same pointer may have opposite designations, as to whether, or not it is horizontal, or vertical, depending on the level of the system under consideration, even within a single application, or system.



Clients and Servers

In our example the BodyDivision class is a client which uses, a Vehicle class as a server (using is a client role) [12]. The decision as to how horizontal pointers are configured at any particular time occurs within clients. Clients use conditional statements to select between hierarchies of vertical, or what is the same thing, implementation servers. The static type of a vertical pointer is determined by a server hierarchy. Which vertical pointer is activated - and hence what vertical system path is taken - is determined by horizontal policy. Intra-layer policy determines inter-layer implementation.

Servers, like SportsVehicle, are implemented in a hierarchy of classes. They are subclasses in an inheritance hierarchy of classes, as SportsVehicle is in the Vehicle hierarchy of classes. Such a hierarchy of classes need not have many levels of inheritance. Often a simple one level hierarchy of siblings inherited from a parent is sufficient. The Vehicle class hierarchy has been constructed to allow clients such as BodyDivision to use any of its server subclasses.

Taking an overall system view, servers may be clients and clients may be servers. The basis for classifying an object as a server, or client depends on the level of the system, and context under consideration.

A major way to view client, and server classes should be as agents of dynamic substitution, and change within a system. Both clients, and servers may dynamically alter the structure, and configuration of a software system. Highly polymorphic systems, through clients, and servers, can flexibly, alter and adapt their structure, and nature to deal with a wide variety of changes in the application environment. It is this which gives highly polymorphic systems, their general, advantage over systems with little or no polymorphic substitution.



The Root Power of Inheritance

Through the use of C++ class, and object inheritance we can easily construct systems which allow both dynamic substitution of one horizontal, or policy class for another, and substitution of one vertical, or implementation class for another. So how exactly is inheritance being taken advantage of to allow this?

Inheritance based dynamic substitution, and polymorphism rest on taking advantage of the commonality[13] found in classes, and subclasses arranged in an inheritance hierarchy, as in our Vehicle example. The commonalities among classes in a hierarchy we want to take advantage of are common responsibility, common interface, and common type.

Polymorphism is generally viewed to be a focus on taking advantage of type commonality. The parent class is seen as a specific type (kind of thing) and its children are seen as specializations, or extensions of the parent's type, as with our vehicle hierarchy of classes. This is the typical is-a relationship found in many object-oriented programs.

Dynamic substitution, on the other hand, should be viewed as being more focused on responsibility, and interface commonality present across a hierarchy, as opposed to polymorphism which additionally emphasizes type commonality. Dynamic substitution casts a wider net than polymorphism. Polymorphism is simply one aspect of dynamic substitution.

Taking advantage of commonality, along with templates, and function name overloading are the major sources of polymorphism for most C++ applications. It should be noted that, taking advantage of a class hierarchy's commonality is the only way to dynamically substitute the horizontal, and vertical components of a C++ application.

The commonality of the class hierarchy is represented by an abstract base class (ABC) in C++. An abstract class has functions which represent the commonality of the class hierarchy. It declares the operations, or functions that subclasses of the hierarchy must define to be a part of the hierarchy. these undefined operations are called abstract, or, pure virtual functions. (While abstract classes must have at least one abstract function declaration, they may have one, or more, non-abstract functions declared as members also.)

A pointer having the compile-time (static) type of the abstract class at the base of the hierarchy, such as the Vehicle base class, is used by clients, such as BodyDivision, to gain services from subclasses in the hierarchy. The client uses the base class pointer to call the abstract functions that each hierarchy subclass defines on its own. Just as in our example, 'vehiclePointer', which is of static type Vehicle, was used by BodyDivision to call abstract functions like Stripe( ) to acquire services, or information from the various subclasses of the Vehicle ABC[14].

All of the classes in a hierarchy, from top to bottom, must require no more, nor provide any less than what the others do. The ABC provides a contract to clients, or users of the hierarchy. This contract specifies, on the one had, what the hierarchy requires to function properly, and on the other, what services, or responsibilities all classes in the hierarchy will provide, or carry out.

Because of the abstract functions an abstract class is said to be an abstract interface. Because the class commonality an abstract base class embodies is the source of a class hierarchy's polymorphism, the writer formulated the term abstract polymorphic interface, or polymorphic interface (PI), to refer to a pointer having the static type of an abstract base classes. This captures the nature of the idiomatic usage of such a pointer in C++.

It would be a mistake to fail to make as much use of the power available through this topological, commonality arrangement of classes as we can. The class hierarchy's topological arrangement should, in most cases, reflect the structure of relations between real abstractions in the application domain. It would be a mistake to fail to use as much inheritance based dynamic substitution as necessary through the use of PI's.

A final word on the root power of inheritance. Considering time and space, inheritance has been found to the most efficient of the various mechanisms used to achieve polymorphism. To quote Budd, "Tomlinson provides a good analysis of the time and space requirements of delegation and inheritance[15], concluding that inheritance is generally faster and, surprisingly, requires less space."[16] Booch, Rumbaugh et al, and others, concur with this analysis[17]. They understand that inheritance based polymorphism is superior, overall, to polymorphism based on other means. So that, dynamic substitution of policy, and implementation via the inheritance mechanism is the most effective and efficient means of doing so.



One Body Two Arms

A nice feature of C++ is that inheritance based polymorphism may underlie both function name, and argument polymorphism[18]. And you can get both features very easily by using pointers of the static type of abstract base classes.

Function name polymorphism in C++ uses an abstract class pointer to dynamically substitute the behavior of one subclass' function for the same name function of another subclass, or object. Here is an illustration of the syntax of function name polymorphism from our previous examples:

vehiclePointer->Stripe( );

Of all of the Stripe( ) functions defined in each of the subclasses of the Vehicle hierarchy, the specific one executed depends on the class type - OffRoadVehicle, SportsVehicle, or VanVehicle - of the specific object 'vehiclePointer' points to when the statement is executed during run-time.

Argument polymorphism uses pointers to a base class, such as Vehicle, as arguments to functions. Again from our example:

stripeOrShip(Vehicle* vehiclePointer);

The argument to the function stripeOrShip( ), 'vehiclePointer', may refer to an object belonging to any one of the Vehicle hierarchy subclasses: OffRoadVehicle, SportsVehicle, or VanVehicle. We are not restricted to passing only variables of one type, or class, as the function's argument.

It is obvious that being able to dynamically substitute any subclass object of the Vehicle class hierarchy into the argument variable, allows us to have a high degree of flexibility in our programs. We can pass into the stripeOrShip( ) function a range of different kinds of objects.

A last word on function name, and argument polymorphism is that they may be combined in a process known as double dispatching. Let's say that we now have derived subclasses of the BodyDivision class in addition to the subclasses we have previously derived from Vehicle. The various BodyDivision subclasses are referred to through the pointer, 'bodyDivisionPointer'. We may now execute a statement such as:

bodyDivisionPointer->Stripe(Vehicle* vehiclePointer);

We have double dispatching because we have, in a single statement during a running program, the ability to execute any Stripe( ) function from amongst all of the objects instantiated from all of the classes derived from BodyDivision, and because during a running program, we can pass as an argument to 'Stripe( )' any object instantiated from any class derived from the Vehicle class.

So with C++ double dispatching we can combine the two forms of polymorphism to create even greater polymorphism in a software system. Double dispatching allows us to specify two dynamic substitutions in a single statement.[19]



A Sparkling Diamond is Born

Creating a class hierarchy should mainly be based upon the analysis phase (logical) "splitting and merging" of application classes. Bjarne Stroustrup, the creator of C++ recommends taking this approach[20]. Doing so helps to make C++ systems more flexible, and extensible.

A key side benefit of creating an analysis phase abstract PI, with its corresponding concrete hierarchy of classes is the breaking of compilation dependency between clients using the PI, and the hierarchy of server classes.

Code implementation within server classes can be changed without having to re-compile client, users of the server classes. This reduces the compilation time of a C++ program, and minimizing compilation time is a very important goal of good, C++, physical design.

There is a dialectic (interaction) between dynamic substitution based on the analysis phase splitting, and merging of classes on the one hand, and breaking physical client/server compilation dependency on the other. The result of the dialectic gives birth to an intensely high degree of adaptability, and robustness for many C++ software systems.



Complex Systems and Dynamic Substitution

Dynamic substitution of a software system's policy, and implementation reflects a part of the behavior of many complex systems in the real world.

An example of dynamic substitution of implementation in real world systems is how the human immune system substitutes various antibodies in its fight against foreign organisms. An example of dynamic substitution of policy in real world systems is how the human immune system decides whether, or not to even send antibodies into a fight. Before fighting, the system must determine whether, or not an organism it encounters should be resisted; it must determine whether, or not the organisms it encounters are a part of the body, or foreign. This points out the efficacy of the dynamic substitution of policy, and implementation in complex systems.

One of the major thrusts of object orientation in software development is the conscious incorporation of significant characteristics of real world complex systems into software systems. Understanding the operation of dynamic substitution in complex systems, and keeping them in mind during software development seems to offer significant benefits21.



Polymorphism and Type Constraint

Of course, in most cases, systems do, and should have limits. Constraints can be applied to a system via its type system. The nature of the configuration of both horizontal, and vertical pointers, at any one time, in a system are partially constrained by the C++ type system.[22] The nature, and extent of what a path of horizontal, and vertical pointers can, or should do is set by the static class (or subclass) types of the pointers in the path.

Of course a subclass function in the path can be overriden in an irresponsible way by a programmer. However, good object-oriented programmers will not attempt to override a draw( ) subclass function by throwing away the drawing functionality and putting in its place telephone answering capability, for instance.[23]

The type system is not only capable of suppressing inappropriate behavior, but can provide other kinds of integrity even within highly flexible polymorphic systems. Specific object types or other entities can be blocked from flowing along certain paths, while others are allowed. Using the type system, certain operations can be allowed and others prohibited, for a variety of circumstances.

We see from the preceding discussion that the combination of C++ inheritance based polymorphism, and the C++ type system provides adaptability, extensibility and system integrity all at once.



Scalability, Components and Dynamic Substitutability

The use of C++ inheritance based dynamic substitutability is scalable. Just as the BodyDivision client class dynamically invoked the functions of various Vehicle hierarchy subclass objects for implementation, subclasses of components, or what are the same thing, categories, and groups of class, may be dynamically selected for substitution, and activation in a system. And just as the BodyDivision client dynamically selected between horizontal policies, components may be dynamically selected for horizontal policy.

So that in a single system, there may exist micro substitution at the class level, and macro substitution at the component level. Component dynamic substitution adds a totally new dimension to the matter of instant by instant system configuration.

Components may be used as a subsystem on their own, or as elements of a larger subsystem of many components. There are four primary ways to group classes into components in C++ and they may intermixed: the '#include' mechanism, the use of file module visibility rules, the new feature of namespaces, and the grouping together of mixin classes into a single aggregate class[24]. It is by using the latter, aggregate class mechanism that we can dynamically substitute one component for another.

Mixin classes get their name from the fact that they are generally not instantiated on their own. They are usually inherited, as a group, into a component interface class, which is then instantiated. The class that mixins are inherited into is called an aggregate class. So here we have a single class which represents a component.

The single aggregate class representing a component may be inherited from to form subclass components in a hierarchy below the first component, aggregate class. These subclasses may include other component, aggregate classes (or even one or more independent, single classes, though that would not be common).

Just as we made use of the hierarchy of subclasses in our Vehicle example, by using a pointer of the type of the base Vehicle class, we can make use of an inheritance hierarchy of components by using pointers to the base component, aggregate class. Deft use of this base pointer allows us to dynamically substitute both horizontal policy components, and vertical implementation components. With pointers to base aggregate components we can access, and exercise class functions, and data in derived aggregate components.

Use of base component pointers allows extremely adaptable use of components on their own, as subsystems, or as elements of subsystems. Employing base component pointers we can dynamically substitute, or swap out, entire subsystems of our program systems. We can replace one subsystem for another during run- time. We can dynamically add, replace, or delete a system's subsystems.[25]

This offers untold flexibility for our programs and systems. In fact, for most C++ systems it is through dynamic substitution of components of policy and components of implementation that decidedly observable differences in system behavior will occur. Nevertheless, dynamic substitution does occur at the class, or object level. And thus we have two major aspects of dynamic substitution in C++ software systems - class, or micro, based on the one hand, and component, or macro, based on the other.

The ability to dynamically swap both components, and individual classes demonstrates that the mechanism of dynamic substitution may be "shared" throughout all layers of a system's hierarchy of layers.[26] The shared mechanism of dynamic substitution may also be present in each of the vertical processes slicing across two, or more horizontal layers of a software system.

Also, just as with classes, components can have a client/server relationship to each other. In a system with a layered architecture, component clients within a horizontal layer make policy decisions for that layer by dynamically substituting component aggregate subclasses. Implementation components in vertical layers provide services for clients through the same process of dynamic substitution.



--------------------------------------------------------------------------------


1 Like the 7 layer ISO Open Systems Interconnect (OSI) Reference Model for communications.

2 Donald Knuth, Literate Programming (California: Center for the Study of Language and Information, 1992) 73.

3 Every class has a constructor which creates runnable copies of the class (objects). Objects will be explained in more detail shortly. In this constructor we are simply initializing the two class variables, 'exportStatus', and 'vehicleType'. C++ class variable initialization should generally be done using a mechanism called an initializer list, but for simplicity we will use ordinary assignment.

4 Although implementation is sometimes referred to as "details", it's better to think of it as "not policy". This gets us away from thinking of "details" as operations necessarily at an assembly language like level of abstraction.

5 Versus private inheritance used mainly for implementation, and not generally for dynamic substitution.

6 Read Barbara Liskov's Data abstraction and hierarchy, SIGPLAN NOTICES 23(5):25, 1988, for more on this.

7 A pointer is a way of indirectly referring to something.

8 See listing 1 for a complete example using the latest Vehicle and BodyDivision classes.

9 The various functions of a class, such as BodyDivision, should generally operate within the same horizontal layer, not vertically across layers.

10 The concept of horizontal, and vertical pointers is a logical construct.

11 The value of calculus integration , for a highly polymorphic system at any instant, varies according to the specific configuration of its horizontal, and vertical pointers at that instant.

12 This is a paradigm of good C++ design we should adopt in most cases. In general we should consciously construct classes and objects as either clients, or servers. We see from our examples that the "using" relationship of the BodyDivision class with respect to a Vehicle may be extended and made dynamic by taking advantage of the commonality, or is-a relationships amongst Vehicle server classes. We will explore more about hierarchy commonality, and is-a relationships in the next section.

13 isomorphism

14 The Stripe( ) function belonging to an object of a Vehicle subclass currently assigned to 'vehiclePointer' is found, in most C++ compiler implementations, by using a virtual table (vtbl) mechanism. The vtbl holds pointers to the various Vehicle subclass implementations of Stripe( ).

15 Chris Tomlinson, Mark Scheevel and Won Kim, "Sharing and Organizing Protocols in Object-Oriented Systems" Journal of Object-Oriented Programming, 2(4): 25-36, 1989.

16 Timothy Budd, An Introduction to Object-Oriented Programming (Massachusetts: Addison, 1988) 276.

17 Grady Booch, Object-Oriented Analysis and Design (California: Benjamin, 1994), Budd, IOOP, James Rumbaugh, Michael Blaha, William Premerlani, Frederick Eddy, and William Lorenson, Object-Oriented Modeling and Design (New Jersey: Prentice, 1991).

18 ad hoc, and pure polymorphism respectively

19 We could add any number of pointer arguments to the function. Then it would more like multiple dispatching.

20 Bjarne Stroustrup, C++ Programming Language (Massachusetts: Addison, 1991).

21 Grady Booch is a leader in promoting the view that we should consciously incorporate the mechanisms of real world complex systems into software systems, and his book OOA&D clearly reflects this.

22 The configuration of a system's pointers are also constrained by the choices made at various conditional locations in both clients, and servers.

23 Though of course in some exceptional cases, doing so may be desirable for whatever reason.

24 Booch, OOA&D Booch discusses the mixin/aggregate mechanism in some detail throughout OOA&D. But especially see 63, 127-8, 373, and 515.

25 How about if we add "self modifying" code to the mix?

26 Booch, OOA&D 10-11.

Copyright 1995, 1996, 1998 Elliott Coates 11/11/98 3:26 PM

----------------------------------------------------------------------------------------------------------

©Bear Killer©
07-06-2002, 09:58 AM
الـ ++c مطورة 15% عن الـ C

واللي تعلم للـ C لن يجد صعوبة كبيرة بينهم

:)