Like Tree4Likes

Overloading, Overriding, and Hiding - help?

This is a discussion on Overloading, Overriding, and Hiding - help? within the C++ Programming forums, part of the General Programming Boards category; As I'm reading various books and online resources I'm seeing different terms used to describe what I think are the ...

  1. #1
    Registered User deoren's Avatar
    Join Date
    Mar 2003
    Posts
    63

    Overloading, Overriding, and Hiding - help?

    As I'm reading various books and online resources I'm seeing different terms used to describe what I think are the same thing, but I can't quite be sure due to my inexperience.

    Thanks in advance for reading this, and my apologies if this is too long.




    I was recently reading C++ Programming: Visual QuickStart Guide, by Larry Ullman and Andreas Signer when I came across this:


    Pay attention when overriding methods. If you don't use the exactly same parameters and return values, you'll end up with an overloaded method, and not an overridden one.
    I tinkered with the code and tried to figure it out, and made a posting on Larry's forums asking for clarification where he shared this StackOverflow link.

    From that link, I took away that I had the following understanding:

    • A derived class function with a different signature than a base class function will result in the base class function of the same name being hidden.
    • A derived class function with the same name and signature as the base class will override the base class function.


    I kept getting hung up on his use of the term overloaded in the quote above, particularly in the context of a derived class function hiding a base class function, so I want to make sure that I have a solid grasp of these terms before proceeding further:


    • Overloading
    • Overriding
    • Hiding




    Overloading

    My understanding of overloaded methods/functions is what C++ Pocket Reference sums up so well on page 104:

    Overloading allows you to provide more than one definition for a function within the same scope.
    If I'm correct, a base class and derived class are separate scopes, so overloading will not occur between the two without explicitly "pulling them in" via a using statement.

    Bjarne Stroustrup had this to say:

    In C++, there is no overloading across scopes - derived class scopes are not an exception to this general rule.
    While I waited for Larry or another forum goer to find my question where I asked for clarification, I went back to chapter 9, Inheritance and Polymorphism from Ivor Horton's Beginning Visual C++ 2008 and finally arrived at this statement on page 539:



    Overriding

    Ivor Horton said replaced, and not overridden, but I am thinking he was referring to the same thing:

    The class also contains the function ShowVolume(), which displays the volume of objects of derived classes. Because this is declared as virtual, it can be replaced in a derived class, but if it isnít, the base class version that you see here is called.
    In Thinking in C++, 2e, Volume 1 (pages 632:646), Bruce Eckel had something similar to say, but he did specifically say overriding:

    The redefinition of a virtual function in a derived class is usually called overriding
    Follow that up with page 130 from C++ Programming in Easy Steps:

    A method can be declared in a derived class to override a matching method in the base class - if both method declarations have the same name, arguments and return type.
    The C++ FAQ Lite entry, "Should a derived class redefine ("override") a member function that is non-virtual in a base class?", recommends against redefining (overriding) a member function that is non-virtual in a base class.

    Hiding

    Page 130 from C++ Programming in Easy Steps also mentions:

    The technique of overriding base class methods must be used with caution however, to avoid unintentionally hiding overloaded methods - a single overriding method in a derived class will hide all overloaded methods of that name in the base class.
    C++ Pocket Reference, 1e (page 93) has this to say:

    The parameter list and return type for the member function in the derived class must match those of the member function in the base class. Otherwise, the member function of the derived class hides the member function in the base class, and no polymorphic behavior will occur.
    Thinking in C++, 2e, Volume 1 (pages 632:646) mentioned this:

    If you override one of the overloaded member functions in the base class, the other overloaded versions become hidden in the derived class.


    Summary

    • redefining, overriding and replacing a function occurs when you have a derived class function with the same function signature as a base class function.
    • Doing so hides all of the other base-class versions of that function.
    • It is not recommended to do this with non-virtual base class functions.
    It is better to fail with honor than win by deceit
    - unknown

    My erratic tinkerings:
    http://projects.whyaskwhy.org/

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    21,931
    Quote Originally Posted by deoren
    Summary
    • redefining, overriding and replacing a function occurs when you have a derived class function with the same function signature as a base class function.
    • Doing so hides all of the other base-class versions of that function.
    • It is not recommended to do this with non-virtual base class functions.
    Not quite. I suggest that you stick to the term "overriding" to describe what happens when you define a member function with the same name and signature in a derived class as a virtual member function in (one of) its base class(es). Understood in this way, overriding a virtual member function only hides the overriden virtual member function from the base class, and it does so in order to be used polymorphically.

    The problem comes when you attempt to do define a member function in the derived with the same name but different signature from the virtual member function in the base class that you actually want to override. In this case, the override does not happen. Rather, you end up uploading the member function, and this overload then causes all member functions from the base class with the same name to be hidden.

    Defining a member function in the derived class that has the same name and signature as a non-virtual member function in the base class is another issue. It is not recommended because it is misleading: it may seem like an override, but in actual fact polymorphism is not involved, i.e., if you call the member function through a pointer to the base class type, the base class version will be called.
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Registered User whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    7,739
    You understand correctly. You can prove what these old men are saying to yourself. In particular, only overriding and overloading have meaning as OOP words. And I would not use the word replaced when talking about overriding personally. Hiding is a better way to refer to it since you can explicitly resolve the scope of the member function. To me, replaced just communicates a member function that is gone and can't be called at all.

    Code:
    	void A::foo() { cout << "called A::foo(void)\n"; }
    	void A::foo(int) { cout << "called A::foo(int)\n"; }
    	void B::foo() { cout << "called B::foo(void)\n"; }
    
    	B b;
    	//b.foo(1);	// a.cpp:20:9: error: no matching function for call to 'B::foo(int)'
    			// a.cpp:14:7: note: candidate is: void B::foo()
    	b.foo();
    	b.A::foo(1);
    If overloading worked across class scope, b.foo(1) would resolve to A::foo(int), but as you can see that does not happen.

  4. #4
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    Overriding, as laserlight says, applies only to member functions in a derived class with the same name and argument types as a virtual function in some base class. (The return type may vary within certain bounds, known as co-variant return types.)
    Overloading is the ability to decide between multiple functions with the same name and different argument types found by name lookup. These must be in the same scope, because lookup stops searching scopes (roughly speaking, a scope is anything delimited by braces) once it finds a name in a scope. So it starts with the innermost scope and searches outwards, and it searches derived classes before base classes. Once something is found, it stops. (Then a special rule may kick in and look some more, but let's ignore that.)
    Hiding just means that there are multiple things with the same name, and the lookup rules mean that one of them isn't found because the other is found first. Hiding isn't mutually exclusive with either overloading or overriding. Hiding is a matter of name lookup, and in fact a variable can hide a function or vice versa, whereas only groups of functions can be overloaded, and only functions can override other functions. Overloading is a matter of deciding at compile time which of a bunch of found functions should be called for a set of arguments. And overriding is a matter of deciding at runtime which of a bunch of functions with the same signature should be called for a given object.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  5. #5
    Registered User deoren's Avatar
    Join Date
    Mar 2003
    Posts
    63
    Quote Originally Posted by laserlight View Post
    Not quite. I suggest that you stick to the term "overriding" to describe what happens when you define a member function with the same name and signature in a derived class as a virtual member function in (one of) its base class(es). Understood in this way, overriding a virtual member function only hides the overriden virtual member function from the base class, and it does so in order to be used polymorphically.
    Understood.


    Quote Originally Posted by laserlight View Post
    and this overload then causes all member functions from the base class with the same name to be hidden.
    Sorry, but I'm still getting hung up on the use of the word overload. From what I understood, overloading only applies to functions within the same scope.


    Quote Originally Posted by laserlight View Post
    Defining a member function in the derived class that has the same name and signature as a non-virtual member function in the base class is another issue. It is not recommended because it is misleading
    (emphasis mine) That makes sense. Basically it's bad practice then?

    Quote Originally Posted by whiteflags View Post
    In particular, only overriding and overloading have meaning as OOP words. And I would not use the word replaced when talking about overriding personally. Hiding is a better way to refer to it since you can explicitly resolve the scope of the member function. To me, replaced just communicates a member function that is gone and can't be called at all.

    ...

    If overloading worked across class scope, b.foo(1) would resolve to A::foo(int), but as you can see that does not happen.
    Great explanation, and the code makes sense.

    I'm off to read more about overloading so I can find my hangup on the issue. To be clear, this is what I believe overloading to be:

    Multiple functions with the same name, but different signatures defined within the same scope.

    I just don't see how that word applies to hiding a base class function from within a derived class.

    Thanks!
    It is better to fail with honor than win by deceit
    - unknown

    My erratic tinkerings:
    http://projects.whyaskwhy.org/

  6. #6
    Registered User deoren's Avatar
    Join Date
    Mar 2003
    Posts
    63
    Thanks for the reply CornedBee.

    Quote Originally Posted by CornedBee View Post
    Overloading is the ability to decide between multiple functions with the same name and different argument types found by name lookup. These must be in the same scope, because lookup stops searching scopes (roughly speaking, a scope is anything delimited by braces) once it finds a name in a scope. So it starts with the innermost scope and searches outwards, and it searches derived classes before base classes. Once something is found, it stops. (Then a special rule may kick in and look some more, but let's ignore that.)
    So, if I'm understanding what you're telling me, in the code provided by whiteflags previously:

    Code:
    	void A::foo() { cout << "called A::foo(void)\n"; }
    	void A::foo(int) { cout << "called A::foo(int)\n"; }
    	void B::foo() { cout << "called B::foo(void)\n"; }
    Assuming that B inherits from A:

    • B::foo() will not overload A::foo() because they're not from the same scope.
    • B::foo() will override A::foo(), causing A::foo() to be hidden to the derived class B if called from an instance of B.
    • A::foo() will be visible from an instance of class A


    I also see the word redefine used quite a bit, but from what I can tell it is interchangeable with override.
    It is better to fail with honor than win by deceit
    - unknown

    My erratic tinkerings:
    http://projects.whyaskwhy.org/

  7. #7
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by deoren View Post

    Code:
    	void A::foo() { cout << "called A::foo(void)\n"; }
    	void A::foo(int) { cout << "called A::foo(int)\n"; }
    	void B::foo() { cout << "called B::foo(void)\n"; }
    Assuming that B inherits from A:

    • B::foo() will not overload A::foo() because they're not from the same scope.
    A method with the same name and signature as another method cannot logically overload it -- overloaded methods must differ from one another in some aspect of their signature. That is the purpose of overloading .

    However, if they are are in the same scope, this is a redefinition error, which the compiler might indicate by saying, "A::foo() cannot be overloaded" -- point being there is no circumstance whereby a method called foo with a signature identical to another method called foo could "overload" it.

    • B::foo() will override A::foo(), causing A::foo() to be hidden to the derived class B if called from an instance of B.
    • A::foo() will be visible from an instance of class A
    The point in red would be considered redundant, I think.

    Here is a more meaningful application of "hiding":
    • A::foo(int) is hidden from class B because the identifier "foo" was overridden in B.


    Whereas A::foo() is overridden, A::foo(int) is just hidden.

    Like whiteflags said, the key OO concepts here are overloading and overriding. "Hiding" is just a consequence of mechanics particular to C++. Fortunately, you can "unhide" methods with a special redeclaration (line 16):

    Code:
    #include <iostream>
    
    using namespace std;
    
    class A {
    	public:
    		A () {}
    		int foo (void) { return 666; }
    		int foo (int x) { return x/2; }
    		int foo (float x) { return (int)x; }
    };
    
    class B : A {
    	public:
    		int foo () { return 42; }
    		A::foo; // reveal all the foo in A
    };
    
    int main(void) {
    	B *b = new B();
    	cout << b->foo(13) << endl;
    	cout << b->foo(1.23f) << endl;
    	return 0;
    }
    Last edited by MK27; 03-01-2012 at 12:10 PM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  8. #8
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    MK27: that special declaration is archaic style. Don't do it. Use a using declaration instead.
    Code:
    class B ... {
      ...
      using A::foo;
    };
    Overloading, overriding, and hiding are independent concepts. They don't really have any influence on each other. Consider:
    Code:
    void fn() {
      B b;
      A& a = b;
      b.foo();
      a.foo();
    }
    When the compiler see "b.foo()", it does the following:
    1) It looks up the name "foo". The context is a member expression, so "foo" is first searched for in B. It finds the function B::foo(). Because it just found something, it stops looking. A::foo() and A::foo(int) are never found; they are, in this context, hidden by B::foo().
    2) Since the compiler only found one function, deciding which to call is trivial. The compiler matches the supplied arguments (none) with the expected parameters (none) and decides that it fits. No overloading happens.
    3) B::foo() is virtual, so a virtual call might be in order. However, b is of the concrete type B (it is not a pointer or a reference), so the compiler knows that no relevant overriding is happening. So nothing special here, either.

    When the compiler sees "a.foo()", things are more interesting.
    1) It looks up "foo". The context is again a member expression (a.foo), so the compiler looks in the context of class A first. There it finds A::foo()" and A::foo(int). Since it found something, it stops looking. However, as class A is the last scope to search (unless class A itself has a base class), there are no functions that are hidden by these two functions anyway.
    2) The compiler found two functions, which means they are overloaded, and overload resolution takes place to decide which is actually used. This ridiculously complex procedure is pleasantly simple in this case: the supplied arguments (none) match the expected arguments of A::foo() (none) perfectly, but don't match the expected arguments of A::foo(int) (an integer) at all, so A::foo() it is.
    3) a is a reference, and A::foo() is virtual, so the compiler must consider overriding. The call a.foo() is virtual, and the compiler generates code that ensures that the right function is called when the program runs. At runtime, it turns out that a refers to b, which is a B object. Since B::foo() overrides A::foo(), B::foo() is the function that actually gets called.
    MK27 and deoren like this.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  9. #9
    Registered User manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    Kolkata@India
    Posts
    2,498
    This thread could become a FAQ entry, I learnt quite a bit from it.
    Manasij Mukherjee | gcc-4.8.2 @Arch Linux
    Slow and Steady wins the race... if and only if :
    1.None of the other participants are fast and steady.
    2.The fast and unsteady suddenly falls asleep while running !



  10. #10
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,249
    Another way of seeing it, is that in overriding, the thing which is being overridden is a FUNCTION. In overloading, the things which is being overloaded is a NAME.

    In other words, if you have this:

    Code:
    void foo(int x);
    void foo(double y);
    You have two functions, not one function. It's the NAME that has more than one meaning. The two functions are distinct, however.

    To make it stupidly easy on yourself, just start thinking of the signature as part of the name, so you realize that foo(int x) and foo(double y) are in fact unrelated apart from both having 'foo' in them
    deoren likes this.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  11. #11
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    To make it stupidly easy on yourself, just start thinking of the signature as part of the name
    Considering "name mangling" that's exactly the case.

  12. #12
    Registered User deoren's Avatar
    Join Date
    Mar 2003
    Posts
    63
    Thanks to everyone who replied with corrections and further explanation. I really appreciate it.

    Quote Originally Posted by MK27 View Post
    A method with the same name and signature as another method cannot logically overload it -- overloaded methods must differ from one another in some aspect of their signature. That is the purpose of overloading .
    Good catch, I *think* I meant to say:
    B::foo() will not overload A::foo() because they're not from the same scope
    That is where I was having some trouble understanding how the word overload could apply to functions from two different scopes.


    Quote Originally Posted by MK27 View Post
    The point in red would be considered redundant, I think.
    I was going for additional text, hoping to explain it a different way. It is sort of how I'm trying to piece together an understanding, such that it is.

    Quote Originally Posted by MK27 View Post
    Here is a more meaningful application of "hiding":
    • A::foo(int) is hidden from class B because the identifier "foo" was overridden in B.


    Whereas A::foo() is overridden, A::foo(int) is just hidden.

    Like whiteflags said, the key OO concepts here are overloading and overriding. "Hiding" is just a consequence of mechanics particular to C++.
    Great explanation, I think I have overriding down pretty good now.


    Quote Originally Posted by CornedBee View Post
    Use a using declaration instead.
    Code:
    class B ... {
      ...
      using A::foo;
    };
    How often is that approach used, and should any warning bells be going off when you find yourself using it? It's probably because I'm new, but it almost seems like a workaround for something else.



    Quote Originally Posted by CornedBee View Post
    3) B::foo() is virtual

    ...

    3)... and A::foo() is virtual,
    How is that? Everything else is explained very well (thank you), but I don't get how those functions are virtual since I didn't spot a virtual keyword. Does this line alone determine that, or was the virtual keyword implied (but not shown) by how the code is used?

    Code:
    A& a = b
    Quote Originally Posted by brewbuck View Post
    Another way of seeing it, is that in overriding, the thing which is being overridden is a FUNCTION. In overloading, the things which is being overloaded is a NAME.
    I'm with you so far.

    Quote Originally Posted by brewbuck View Post
    In other words, if you have this:

    Code:
    void foo(int x);
    void foo(double y);
    You have two functions, not one function. It's the NAME that has more than one meaning. The two functions are distinct, however.

    To make it stupidly easy on yourself, just start thinking of the signature as part of the name, so you realize that foo(int x) and foo(double y) are in fact unrelated apart from both having 'foo' in them
    That makes sense.




    I'm going to go back and retype my notes to make sure I have a solid understanding of the differences between overriding and overloading, and will keep going back and forth with Larry Ullman on this thread until he either gets tired of me or I finally "get" what he was saying here:

    Pay attention when overriding methods. If you don't use the exactly same parameters and return values, you'll end up with an overloaded method, and not an overridden one. Such mistakes are very hard to debug!
    It is better to fail with honor than win by deceit
    - unknown

    My erratic tinkerings:
    http://projects.whyaskwhy.org/

  13. #13
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by deoren View Post
    Good catch, I *think* I meant to say: "B::foo() will not overload A::foo() because they're not from the same scope"
    That *is* what you said. It is true in a sense, but it still misses the point that two functions with the same signature cannot meaningfully overload one another. They override one another.

    Maybe you need to better understand the purpose in order to understand the rules and effects? The purpose of method overloading is to simplify a class interface such that you can have multiple methods with the same name (and presumably, a similar purpose) but different parameters. For example, it is common to overload constructors (in which case, the possibility of overloading the same "name" is essential). Let's say you have a class time, and you want to allow someone to instantiate a time object with either:

    Code:
    time::time (int hours, int minutes, int seconds);
    //or 
    time::time (string when);
    You might specify in the documentation that the string should be of the form HH::MM::SS. So the point here is that overloaded methods have different signatures. B::foo() and A::foo() do not.

    How often is that approach used, and should any warning bells be going off when you find yourself using it? It's probably because I'm new, but it almost seems like a workaround for something else.
    It kind of is a work-around for the aforementioned C++ mechanics, but there is nothing wrong with that; it's certainly much tidier than any alternative I can think of. If you have a base class with a set of overloaded methods, and you want to add another such method in a derived class, by default because of the scoping effects CornedBee etc described, the other base methods will be hidden. That is often not desirable -- you probably want the derived class to inherit the other methods as well. The "using" thing is a pretty simple solution to that.

    Pay attention when overriding methods. If you don't use the exactly same parameters and return values, you'll end up with an overloaded method, and not an overridden one. Such mistakes are very hard to debug!
    Overriding is often used to implement polymorphism, multiple classes derived from the same base, with the same interface, but slightly different behaviour. A classic example is class Animal, class Dog, and class Cat, all of which implement a method:

    Code:
    void speak();
    But when a cat speaks, it says meow. When a dog speaks, it says woof. If Animal::speak() for some reason is not virtual, and you define Dog::speak with a different signature, eg:

    Code:
    void speak(int x);
    this could lead to confusion: people who are used to using the speak method in classes derived from Animal will do this:

    Code:
    dog->speak();
    And instead of a woof, they'll get the default animal sound. This example is contrived; probably the most common screw-up here is something like this:

    Code:
    Animal::whatever (int a, float b);
    Dog::whatever (float b, int a);
    Putting the arguments in the wrong order. Dog::whatever does not properly override Animal::whatever.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  14. #14
    Registered User deoren's Avatar
    Join Date
    Mar 2003
    Posts
    63
    MK27,

    I'm going to reply back to your recent post later today (about to have to leave for something), but I realized I made the same mistake again due to a failure in multi-tasking:

    I ended up saying this in my last post:

    B::foo() will not overload A::foo() because they're not from the same scope
    when I know I meant to say:

    B::foo() will not overload A::foo(int) because they're not from the same scope
    That's what I get for trying to do multiple things.
    It is better to fail with honor than win by deceit
    - unknown

    My erratic tinkerings:
    http://projects.whyaskwhy.org/

  15. #15
    Registered User deoren's Avatar
    Join Date
    Mar 2003
    Posts
    63
    Quote Originally Posted by MK27 View Post
    It kind of is a work-around for the aforementioned C++ mechanics, but there is nothing wrong with that; it's certainly much tidier than any alternative I can think of. If you have a base class with a set of overloaded methods, and you want to add another such method in a derived class, by default because of the scoping effects CornedBee etc described, the other base methods will be hidden. That is often not desirable -- you probably want the derived class to inherit the other methods as well. The "using" thing is a pretty simple solution to that.
    Thank you, that makes a lot of sense.

    I also worked through the example classes you mentioned and got the behavior you described, and was reminded of the same example in Larry Ullman's book. I believe I understood what he was trying to teach the reader, but the use of overload in the explanation is giving me a hard time.

    If you don't use the exactly same parameters and return values, you'll end up with an overloaded method
    He replied back earlier with this:

    Perhaps you're overthinking all this? All I'm really trying to say in that tip is that if you're trying to override a method, make sure that the signature is exactly the same or else things can get funky.

    How's that for the same sentiment without using the other "O" word?
    He's right, I'm too focused the wording he used in the book instead of the explanation itself. I'll take notes from what you guys explained earlier and hit the books/exercises again.

    Thanks for all of the help.
    It is better to fail with honor than win by deceit
    - unknown

    My erratic tinkerings:
    http://projects.whyaskwhy.org/

Page 1 of 2 12 LastLast
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. overriding new and delete
    By gesangbaer in forum C++ Programming
    Replies: 28
    Last Post: 07-27-2010, 02:59 PM
  2. Overriding
    By vb.bajpai in forum C++ Programming
    Replies: 4
    Last Post: 07-11-2007, 04:28 AM
  3. Overriding '+' operator
    By cunnus88 in forum C++ Programming
    Replies: 6
    Last Post: 11-14-2005, 03:24 PM
  4. Overriding OnEraseBkgnd
    By durban in forum C++ Programming
    Replies: 0
    Last Post: 11-01-2005, 11:44 AM
  5. overloading and overriding
    By Unregistered in forum C++ Programming
    Replies: 2
    Last Post: 06-16-2002, 02:29 PM

Tags for this Thread


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21