![]() |
|
![]() |
|
|
Thread Tools | Display Modes |
|
|
#1 |
|
Programmer
Join Date: Jan 2005
Location: Albany, NY
Posts: 43
Rep Power: 0
![]() |
Could some please explain classes to me...
I've read a few tutorials online about classes in c++ and I cannot seem to get a tight grip on the concept. As of now, I realize that classes are used to represent objects (for the purpose of this thread, lets say a duck), and inside these classes there are various functions and variables that pertain to duck. I still don't see how classes can be applied to my application though. The tutorials I've read so far show me how to create the class, but don't explain the code line for line, and don't show how to actually use it.
Could someone please explain the concept behind classes, and show an example, in a dumbed-down manor, of how to use them.? |
|
|
|
|
|
#2 |
|
Hobbyist Programmer
Join Date: Jul 2005
Posts: 158
Rep Power: 0
![]() |
Okay I will cover methods and classes in one stone. Classes are an OOP object. This object can be any thing and can contain more objects. I won't use a duck but a dog.
class mydog
{
private:
mydog()
{
}
~mydog()
{
}
bool barks=true;
bool bites=true;
bool loveypuppy=true;
public:
bool does_bark();
bool does_bite();
bool is_a_lovey_puppy();
};
definitions
mypuppy::does_bark()
{
return barks;
}
mypuppy::does_bite()
{
return bites;
}
mypuppy::is_a_lovey_puppy()
{
return loveypuppy;
}main
#include <iostream>
using namespace std;
int main()
{
mypuppy puppy; //creating a mypuppy instance called puppy
if (puppy.does_bite())
cout << "Oww";
if (puppy.does_bark())
cout << "Shut up";
if (puppy.is_a_lovey_puppy)
cout << "I love my puppy.";
system("pause");
}P.S. Buy a good book I recommend C++ Programming For the Absolute Beginner.
__________________
Geeks may not be cool now but in the long run they prosper. |
|
|
|
|
|
#3 |
|
Programming Guru
![]() Join Date: Jun 2005
Location: Adelaide, South Australia
Posts: 1,260
Rep Power: 5
![]() |
OO (Object Oriented) design is based on the premise that entities are "objects" and things that happen result from the way objects behave and interact with each other. A "class" is a category of object, and each object belongs in a category (in OO-speak, "object belongs in a category" is turned around as "each object is an instance of a class"). Let's say we have two ducks, named for sake of discussion "Donald" and "Daisy"; in an OO model, the one described as "Donald" would be a different object from the beat called "Daisy", but both are still ducks.
Now, in C++, a class is (in computer-speak) a data structure. It defines attributes (eg legs, wings, webbed feet, a bill, etc) that every duck has. And both "Donald" and "Daisy" would be different ducks: they don't share legs, wings, feet, bill, or other characteristics with each other (and, in fact, their wings may be objects in their own right, if we model it that way, and the duck would be described in OO-speak as "an aggregate of wings, feet, ..."). And the duck has behaviour (eg flying, courtship behaviour, swimming, quacking, etc). Some of those behaviours involve interaction with other objects (eg courting involves an interaction with another duck, swimming involves interaction with a body of water), so if we're defining a Duck class we might (depending on what our application is to do) define a Duck as a C++ class; class Duck
{
public:
Duck(const std::string &myname, bool is_male) : name(myname), male(is_male) {};
void Swim(BodyOfWater &);
void Court(Duck &object_of_affection);
Location Position() {return position;};
void SetPosition(Location l) {position = l;};
private:
std::string name;
bool male;
Location position; // Location may be represented as a class too
// other attributes representing characteristics of the duck
void DoCourtshipDance(Duck &object_of_affection);
// and implementatiuon of some behaviour internally
};For example, one implementation of Court() might be void Court(Duck &object_of_affection)
{
if (male && !object_of_affection.male) return; // drakes court ducks
DoCourtshipDance(object_of_affection);
}int main()
{
Duck donald("Donald", true); // a drake
Duck daisy("Daisy", false); // a female duck
Lake lake; // a body of water
bool carry_on = true;
// place donald and daisy in some random positions in the lake
while (carry_on)
{
donald.swim();
daisy.swim();
if (Distance(donald.Position(), daisy.Position()) < 20) // daisy < 20 metres from donald
{
donald.Court(daisy); // donald is one desperate duck
carry_on = false;
}
}
return 0;
}The key in any object-oriented design is to define your categories of objects, what attributes they have, and how they interact with each other. And then create various objects and make them interact. It is not necessary to be comprehensive. For example, the attributes and behaviour a duck has in a kids game will be significantly different from the attributes and behaviour we'd want it to have in a program for managing a duck farm. One other characteristic of classes is that one class can be a specialised form of another. For example, in the above, a Lake is a specialised type of "BodyOfWater". This sort of relationship is represented in C++ classes using inheritence; class BodyOfWater
{
// define generic behaviour and attributes of a BodyOfWater
}
class Lake : public BodyOfWater
{
// define specific behaviour and attributes of Lake
// these may be inherited directly from BodyOfWater
// eg size, location, etc
// or may not (eg number of catfish it contains)
} |
|
|
|
|
|
#4 | |
|
Resident Grouch
![]() ![]() ![]() ![]() ![]() ![]() Join Date: Jun 2005
Posts: 6,453
Rep Power: 10
![]() |
Quote:
Seriously, Grumpy, some responses (such as this) need to be aggregated in one place for future reference. Maybe I'll get off my dead butt and put some effort into it. Demystifying design is a Good Thang. I could attribute it to Thaminda, right? ![]()
__________________
Abstraction doesn't make it impossible to write bad code; it makes it possible to write superior code. Contributor's Corner: Grumpy on C++ Exceptions DaWei on Pointers |
|
|
|
|
|
|
#5 |
|
Programming Guru
![]() Join Date: Jun 2005
Location: Adelaide, South Australia
Posts: 1,260
Rep Power: 5
![]() |
I'll happily support you if you want to aggregate things like this into one place. I'll even help; my only issue is working out where to aggregate it.
Attribution to Thaminda doesn't cover it. A dedication to Thaminda, OTOH, ![]() |
|
|
|
|
|
#6 |
|
Programming Guru
![]() Join Date: Jun 2005
Location: Adelaide, South Australia
Posts: 1,260
Rep Power: 5
![]() |
Dawei, as an example of the dedication ...... howsis???
This text (unrelated to the original question on classes) is something I threw together when a bit bored a few month back, but never got around to posting...... I've just topped it with a dedication <evil grin>. Using exceptions Dedication: Several authors, including myself, generated posts like this one a couple of years back on devshed forums. The intent of the posts was to help both novice and expert programmers, and they were written by volunteers. This post is therefore dedicated to Thaminda, the site admin at devshed who saw fit to claim ownership of the material and reposted it under his (or her) own name without any attribution or acknowledgement and (worse) making it completely pointless for the authors to correct any errors in the original posts. Before getting into advanced concepts related to using exceptions, I'll describe the basics. Most of the following may be gleaned from basic texts, although I've worded things slightly differently. This material starts with basics, and becomes moderately advanced. Basic knowledge of C++ syntax is assumed. Exceptions, as an error reporting and recovering mechanism, are a feature of C++ (not C). As described in many texts, the basic usage comes down to try
{
if (some_condition)
{
ExceptionType x;
throw x;
}
}
catch (ExceptionType x)
{
// recover from error
}1) They must be caught, or the program will exit (via a call to terminate() which, by default, calls abort()). 2) The throwing of exceptions need not be in the same function as the try/catch block. 3) When an exception is thrown, control is passed to the first matching catch() clause for recovery. Any objects local to intervening functions are also destructed. 4) Exceptions may be caught via value or reference. If caught by reference, they may be used polymporphically. 5) The exception types that may be thrown by a function may be specified. If an exception is thrown within the function that does not match the "throw specification", the program will exit via a call to abort(). 6) Within a catch handler, a new exception may be thrown or the caught exception may be rethrown. These features are exhibited in the following sample; #include <string>
class X
{
public:
int Type() const {return 1;};
};
class Y : public X
{
public:
int Type() const {return 2;};
};
;
class Other {};
void FunctionThatThrows() throw (X)
{
Y y;
throw y; // OK; Y is derived from X
}
void AnotherFunctionThatThrows() throw (X)
{
Other a;
throw a; // will result in call to abort();
}
void IntermediateFunction(bool call)
{
std::string hello("Hello");
// just to illustrate how to do it, we catch all exceptions and rethrow them
// Such a construct gives the same effect that the compiler would give us
// anyway
try
{
if (call) FunctionThatThrows();
// the string hello will be cleaned up (i.e. destructed) under all circumstances
// if an exception is not thrown (eg call is false) it will be cleaned up
// if an exception is thrown (eg call is true) it will still be cleaned up
}
catch (...)
{
throw; // rethrow exception
}
}
int main()
{
try
{
std::string x("x");
IntermediateFunction(false);
std::string y("y");
// both x and y will be cleaned up
}
// no exception thrown, so neither of the catch clauses will be invoked
catch (X &x)
{
std::cout << x.Type() << std::endl;
}
catch (...)
{
}
try
{
std::string x("x");
IntermediateFunction(true); // this will throw
std::string y("y"); // so this line is never executed
// but x will still be cleaned up (i.e. it's destructor will be invoked).
}
// an exception of type Y (derived from X) will be thrown, so the first catch clause
will have effect
catch (X &x)
{
std::cout << x.Type() << std::endl; // exception of type Y thrown, so value of 2
printed
}
catch (...) // catch clauses checked in order they are declared.
// First clause matches, so this one has no effect
{
}
return 0;
}Rule 1: View exception handling as part of an error management strategy Exceptions are not the only way of handling or reporting error conditions. Other ways include; 1) When an error condition is detected, simply exit the program. This approach is often used in C programs when dynamic memory allocation fails. 2) Use return or error codes from functions rather than exceptions. This approach is commonly used in C and C++ for errors that occur with file input and output. Both of these approaches may be used, whether exceptions are used or not. The first approach is common in C programs as C does not support exceptions, but less common in well-designed C++ code as an unhandled exception gives the same effect, but also provides an opportunity to catch and recover from the error. The second approach is appropriate if the error condition is not severe. This is a double edged sword, as it requires the caller to deliberately check if an error has occurred, but the error will be silently ignored if the caller forgets to do that check. An error code is therefore appropriate if the program can sensibly continue executing even if the error code is not checked. An example where error codes are appropriately used is "end of file" (EOF) condition with C and C++ file input. This allows parsing of a file using a loop that repeats until EOF is reached. Forgetting to check for EOF is also rarely a critical error (the program can often continue sensibly afterwards). One of my pet peeves with the Java I/O library is that it throws exceptions for some non-critical errors, forcing code that reads from a file to be more complex (several local try/catch blocks) than necessary. It often makes sense to handle errors by different mechanisms. There is nothing wrong with a program that makes use of multiple error reporting mechanisms. Essentially, the errors encountered by an application fall into four categories. 1) Those that can be recovered from immediately, by the code that detects them. 2) Those that cannot be recovered from immediately, but do not cause chaos if they are ignored. 3) Those that cannot be recovered from immediately, but will cause chaos if recovery is not initiated. 4) Those that are irrecoverable. An example of the first would be a function that dereferences a pointer detecting that the pointer is NULL. Such an error might be recovered from silently by doing nothing if the pointer is NULL. This would only be appropriate if behaviour of other code will not be adversely affected by our function silently takes no action. An example of the second is file I/O. Encountering an EOF is often an expected, and non-critical, event when reading a file. An example of the third category is a function that detects failure of supplied data to comply with a complex set of required pre-conditions, but has insufficient information to be able to correct the data. Throwing an exception is appropriate to return control to a caller that (presumably) provided the required data. To develop a useful error handling strategy, it is necessary to characterise the severity of errors, the urgency and feasibility of recovery, and whether or not it is possible to correct an error at the point it is detected. If a particular error falls neatly into one of the categories above, the approach to handle that error will be obvious. A lot of errors will not fall neatly into only one category. In this case, an analysis of of benefits and costs will determine options to realistically handle the error. Rule 2: Use features of the language to help simply exception handling The most helpful language feature is the fact that all locally constructed objects will be destroyed when a function returns (or a block - delimited by {} - completes) whether an exception is thrown or not. This means that, if an exception is thrown from deep within a set of function calls, the destructors will be called for every object created within those functions. This allows the use of RAII (Resource Acquisition Is Initialisation) techniques. Essentially, this means that resources used by an object (memory, file handles, mutexes, etc) are acquired by constructors, and cleaned up by destructors. As the destructors will be called as control passes from a throw statement to the first matching catch clause, this means that all resources grabbed in between will be cleaned up. While member functions may be used to reinitialise the resource (eg a class working with an array may dynamically resize the array), those functions must ensure that they do not cause a leak, and ensure that the destructor will have something valid to clean up. The language also guarantees that, if an exception is thrown while an object is being constructed, that any parts of the objects successfully constructed will be destroyed. This can be used to avoid resource leaks. For example; class Cat {}; // definition of Cat is incidental to this example
class Base
{
public:
Base() : x(new Cat) {};
virtual ~Base() {delete x;};
private:
Cat *x;
};
class Dog {};
class Derived : public Base
{
public:
Derived(): Base(), y(new Dog) {throw Foo;};
~Derived() {delete y;}
private:
Dog *y;
}This rule allows one to avoid the common, but naive, "two phase construction" approach, which essentially means that a constructor sets the object into some default "safe state", and then requires calling of something like an init() function to initialise the object into the needed state. The problems with such an approach include the possibility of forgetting to call the init() function, the possibility of calling it more than, and (often) the need for every member function to check that the object is not in the "default" state before using it. These problems increase likelihood of errors, and make the class implementation more difficult to understand and maintain. The most usual reason for "two phase construction" is avoiding grabbing resources before they are needed. This can also be avoided by not constructing the whole object until it is needed. This practice also simplifies the provision of multiple functions by one class. In this example, class Base manages an instance of a Cat, so the implementer of class Derived need not worry about the creation and destruction of that Cat. Rule 3:Practice exception safety Exception safety amounts to coding your program in ways to provide certain guarantees of behaviour when an exception occurs. It is useful to design operations in terms of the guarantees they provide if an exception is thrown. The guarantees that are provided by operations in the standard library are; 1) The strong guarantee means that an operation succeeds or, if it fails, has no effect. 2) The basic guarantee means that the object remains in a valid state, can be destructed, and no resources are leaked. 3) The nothrow guarantee means that an operation is guaranteed to never throw exceptions, as well as not introducing resource leaks or leaving the object in an invalid state. In other words, if an exception is thrown within a function, it is recovered from and the exception is not propagated. An example where the strong guarantee would be needed is in banking. A transaction on a savings account should either succeed or, if it fails, have no effect. An unauthorised withdrawal should not occur, and not change the account balance. The basic guarantee is a bit weaker. If an error occurs, the object can be successfully reassigned or destroyed, but it's contents may be unpredictable. These guarantees come at different cost. The strong guarantee is the most expensive, as it can mean backtracking to a previous known state when an error occurs. The basic guarantee is the minimum that one should aim for, as it is otherwise not possible to use the object again. All operations in the standard library provide one of the above guarantees, subject to user-defined objects also providing similar guarantees. For example, std::vector<X>::push_back() [used to add an element to the end of a vector] always provides the strong guarantee. However, all variants of std::vector<X>::insert() [used to insert elements into the middle of a vector] only provide the strong guarantee if the copy constructor of X provides the nothrow guarantee. Multi-element insertions into a std::map only provides the basic guarantee. The tools that we, the humble programmer, have to provide these guarantees are; 1) the try/catch block. For example, one way of implementing a nothrow guarantee is to catch an exception, recover from it, and not throw anything again; 2) the RAII technique mentioned above; 3) never let go of a piece of information until we can store its replacement without triggering an exception; and 4) ensure all objects are in a valid state before throwing an exception; 5) ensure all resources that may need to be released are owned by an object (so that object's destructor will release the resource) or release any unowned resources immediately before throwing an exception. An example To illustrate, let's consider how we might implement the constructors and assignment operator for a class that dynamically allocates a dynamically allocated array. First, I'll give a commonly used approach, but one which does not provide useful guarantees. // example of poorly constructed resizable vector
template<class X> class PoorResizableVector
{
public:
PoorResizableVector(int default_size);
void Resize(int size);
PoorResizableVector(const PoorResizableVector<X> &);
PoorResizableVector<X> &operator=(const PoorResizableVector<X> &);
~PoorResizableVector();
private:
X *data;
int size;
};
template<class X> PoorResizableVector<X>::PoorResizableVector(int default_size)
{
data = NULL;
size = 0;
Resize(default_size);
}
template<class X> void PoorResizableVector<X>::Resize(int newsize)
{
if (newsize > size)
{
double *newdata = new X[newsize];
if (size > 0)
for (i = 0; i < size; ++i) newdata[i] = data[i];
size = newsize;
delete [] data;
data = newdata;
}
else if (newsize < size)
{
size = newsize;
}
}
// copy constructor
template<class X> PoorResizableVector<X>::PoorResizableVector(const PoorResizableVector<X> &a)
{
data = new X[a.size];
size = a.size;
if (size > 0)
for (i = 0; i < size; ++i) data[i] = a.data[i];
}
template<class X> PoorResizableVector<X> &PoorResizableVector<X>::operator=(const
PoorResizableVector<X> &a)
{
if (this == &a) return *this;
data = new X[a.size];
size = a.size;
if (size > 0)
for (i = 0; i < size; ++i) data[i] = a.data[i];
return *this;
}
template<class X> PoorResizableVector<X>::~PoorResizableVector()
{
delete [] data;
}The basic problem is that the logic of PoorResizableVector is muddled, and no care is taken to ensure exception safety. Modifying this example, without changing the core logic, to make it provide useful guarantees would actually be quite difficult. For example, when resizing an array, it is necessary to copy existing elements into the resized array. If an exception occurs, it needs to be caught and the memory allocated for the new size array needs to be released. I leave that as an exercise..... A better example is as follows. // example of better constructed resizable vector
template<class X> class BetterResizableVector
{
public:
BetterResizableVector(int default_size = 10);
BetterResizableVector(const BetterResizableVector<X> &);
BetterResizableVector<X> &operator=(const BetterResizableVector<X> &);
~BetterResizableVector();
X &operator[](int index);
private:
X *data;
int size;
};
template<class X> BetterResizableVector<X>::BetterResizableVector(int default_size):
data(new X[default_size]), size(default_size)
{
}
// copy constructor
template<class X> BetterResizableVector<X>::BetterResizableVector(const
BetterResizableVector<X> &a): data(new X[a.size]), size(a.size)
{
for (i = 0; i < size; ++i) data[i] = a.data[i];
}
template<class X> BetterResizableVector<X> &BetterResizableVector<X>::operator=(const
BetterResizableVector<X> &a)
{
BetterResizableVector<X> temp(a);
// swap contents of temp and *this.
X *ptemp = data;
data = temp.data;
temp.data = ptemp;
int tsize = size;
size = temp.size;
temp.size = tsize;
// the preceding 6 lines may be more simply expressed as:
// std::swap(size, temp.size); std::swap(data, temp.data);
return *this;
}
template<class X> BetterResizableVector<X>::~BetterResizableVector()
{
delete [] data;
}It must be noted that this approach comes at a cost: an additional object (and associated resources) must exist while the assignment operator is doing its work. In a lot of cases, safer code is sufficient to justify a short term requirement for additional resources. In some cases, it is not, and there is a trade-off between performance and the type of guarantee made when an exception is thrown. The key, however, is that the type of exception that may be thrown by each line of code needs to be analysed, and the feasibility of recovering from it determined. In the example given, the order of operations (create a complete copy, swap over the internal representations) results in cleaner code because, if an exception occurs, the original object is left unchanged. An alternate implementation of the assignment operator, which actually does almost the same thing, is; template<class X> BetterResizableVector<X> &BetterResizableVector<X>::operator=(const
BetterResizableVector<X> &a)
{
X *tempX = new X[a.size];
try
{
for (int i = 0; i < a.size; ++i) tempX[i] = a.data[i];
}
catch (...)
{
delete [] tempX;
}
delete [] data;
data = tempX;
size = a.size;
return *this;
}Other considerations There are some other considerations related to using exceptions effectively. The C++ standard effectively disallows having two active exceptions at one time. If an exception is thrown while control is being passed back up the call stack because of an existing exception, the program is required to terminate(). In practice, the only ways in which an exception can be generated while another is active is from an objects destructor. This means that throwing an exception from a destructor is something to avoid. In other words, destructors should provide a "nothrow" guarantee. The C++ standard library also strongly discourages the practice of throwing exceptions in destructors, but for different reasons: recovering from exceptions in destructors of an arbitrary user-supplied type is quite difficult to implement and carries a significant run time cost. An example of this is class MyException{};
class Foo
{
public:
~Foo() {throw MyException();};
};
void Func()
{
Foo x;
throw MyException();
}
int main()
{
try
{
Func(); // this will result in program termination
}
catch (...)
{
// we will never get here
}
return 0;
}The basic mechanism by which exceptions are propagated to callers is compiler dependent. However, the C++ standard allows invoking of the exception's copy constructor in that process. This means it is necessary to ensure that copying of exception types cannot throw exceptions (the result may be a call to abort()). It is also not a good idea to define exception types so that constructing them is able to throw an exception. If this is done, an exception may occur in the process of constructing an exception, and the caller might be thrown a different exception from that intended. A side effect of this is that it is not a good idea to use dynamic memory allocation when creating an exception type. This means it is a poor idea to use types like std::string as members of an exception type. For example; #include <string>
struct MyException
{
std::string type; // BAD: construction of this may throw
};
void Func()
{
MyException e; // either line in this function may throw
e.type = "Hello";
throw e;
}Acknowledgements: Although this material is simplified and structured a little differently, significant parts are based on Appendix E of the "The C++ Programming Language, Special Edition" by Bjarne Stroustrup, 2000, ISBN 0-201-70073-5. |
|
|
|
|
|
#7 |
|
Resident Grouch
![]() ![]() ![]() ![]() ![]() ![]() Join Date: Jun 2005
Posts: 6,453
Rep Power: 10
![]() |
The first thought that comes to mind is the C/C++ tutorial section. An examination of that section leads me to conclude that it is not the place. Some of the material is self-promotion. Some is as simple as "Dev-Cpp is a good, free compiler." Much of the actual tutorial material, not having been subjected to peer review prior to posting, contains errors or misleading information. I (personally, your mileage may vary) would not be looking for an air of total C/C++ expertise and guruship, but I would be looking for an air of essential correctness accompanied by a solid approach to presentation, and a restriction of content to tutorial or informative material.
The approach we had at DS (that eventually flew south) was reasonably workable; it still had problems. There, we merely posted in a "Commonly Asked Questions" thread. The ability to edit our own posts remained live (until the thread was coopted, moved, and attributed to another). The problem with the approach was that some people tended to post questions there, rather than start a thread. This required occassional appeals to the moderators to 'clean it up.' Hosting the material on another domain presents a couple of problems. One is providing the right mix of access provision and restriction. The other is a perception of competition. If the material couldn't be readily linked to as a reference without contravening a forum's rules it wouldn't be nearly as useful and wouldn't present an incentive to potential contributors. I'm open to suggestions from the members of the forum and the moderators and administrators.
__________________
Abstraction doesn't make it impossible to write bad code; it makes it possible to write superior code. Contributor's Corner: Grumpy on C++ Exceptions DaWei on Pointers |
|
|
|
|
|
#8 | ||
|
Newbie
Join Date: Feb 2006
Posts: 20
Rep Power: 0
![]() |
a dumbed-down explanation
You may feel comfort in the fact that the majority of people also found themselves banging their head against a wall at one stage or another when they first tried to grasp OOP (I know I did).
Quote:
![]() Classes are essentially blueprints for objects. In exactly the same way that a bunch of houses on a housing estate will all share the same blueprint plans, but a blueprint plan is not a house. Quote:
When thinking in OOP terms, you should focus your attention on the type of information you have to handle, and ignore the nitty-gritty detail of the actual data which your program will eventually store - in fact, this idea, sort of "selective ignorance", is quite central to understanding OOP, and relating it to any problem OOP excels in many ways - one in particular i'd like to mention - Vastly cutting down your usage of if/else sequences. When you want to perform the same or similar operation on otherwise different types of data, Providing an "inherited" or "derived" code link between the different data types will let you perform a magic trick - calling many different functions using just 1 function name. for example, you can potentially cut this chunk of code if(duck==swan) { swanFunction(); }
else if (duck==goose) { gooseFunction(); }
else if (duck==heron) { heronFunction(); }
else { otherFunction(); )duck->duckFunction(); in the above example, you are not concerned with "what sort of species is duck?" because it doesn't matter - you have selectively ignored the species. the class structure will automatically determine which version of the duckFunction() is called. You have saved yourself from a bunch of 'if' and 'else' statements, and hopefully acquired some peace of mind of not having to remember which function is needed for each species. Another way to think about OOP is like driving a car - you could ask yourself 2 questions - (1) How do you change gear in a car? .. or .. (2) How does a car change gear? I could answer question 1, simple, I reach the correct speed, and then I push the clutch down, and then I move the gearstick to the required position. I can also safely say that it does not matter what car I am in, because I use exactly the same procedure regardless of the make & model of my car. I couldn't answer question 2, because I have no idea what happens under the bonnet when I change gear in my car, but I'm still quite capable of driving my car safely... in fact, I'd rather not think about what my engine is doing when I change gear, because I'm more concerned about whether I need to stop or slow down for a pedestrian crossing the road ahead... or that police car flashing its lights behind me... ![]() In case you are wondering about the class structure for my duck example class duck { // the "generic" duck class
public:
virtual void duckFunction(); // the "generic" duck function;
}
class swan : public duck { // swan "inherits" or "derives" from duck
public:
void duckFunction(); // Override the duckFunction() inherited from duck
}
class heron : public duck {
public:
void duckFunction();
}
class goose : public duck {
public:
void duckFunction();
}
int main()
{
duck * MyDuck; // (pointer to) a "Generic" duck
MyDuck = new swan; //create a new duck of the type "swan"
//MyDuck now points to a swan
MyDuck->duckFunction(); // calls swan::duckFunction()
delete MyDuck; // Bye bye swan!
} |
||
|
|
|
|
|
#9 |
|
Programmer
Join Date: Jan 2005
Location: Albany, NY
Posts: 43
Rep Power: 0
![]() |
Thanks to everyone for the replies. I've been writting some small, no practical classes and I pretty much have a good grip on how to write such code.
I still haven't introduced any into my apps though.
__________________
meh... |
|
|
|
|
|
#10 | |
|
Expert Programmer
Join Date: Aug 2005
Location: Rotterdam, the Netherlands
Posts: 942
Rep Power: 4
![]() |
Quote:
|
|
|
|
|
![]() |
| Bookmarks |
| Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
| Thread Tools | |
| Display Modes | |
|
|