Tài liệu Giáo trình C++ P3 pdf

47 540 1
Tài liệu Giáo trình C++ P3 pdf

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Week Game Institute Introduction to C and C++ by Stan Trujillo www.gameinstitute.com Introduction to C and C++ : Week 3: Page of 47 © 2001, eInstitute, Inc You may print one copy of this document for your own personal use You agree to destroy any worn copy prior to printing another You may not distribute this document in paper, fax, magnetic, electronic or other telecommunications format to anyone else This is the companion text to the www.gameinstitute.com course of the same title With minor modifications made for print formatting, it is identical to the viewable text, but without the audio www.gameinstitute.com Introduction to C and C++ : Week 3: Page of 47 Table of Contents Introduction to C++ .4 Lesson – Classes Member Functions .6 Encapsulation 12 Construtors 14 Destructors 16 The this Pointer 20 The Assignment Operator 20 Overloaded Operators 24 Copy Constructors .24 The CmdLine Sample 25 Inheritance 32 Inheritance Syntax .34 Derived Functionality 35 Virtual Functions .37 The VirtualPlayer Sample 41 What’s next? 47 www.gameinstitute.com Introduction to C and C++ : Week 3: Page of 47 Introduction to C++ A GameInstitute course by Stan Trujillo In Lesson we discussed functions In Lesson we discussed data structures In this lesson, we introduce classes, which blend functions and data structures to create a powerful language feature: objects Using objects, entities can be created that are intelligent and robust in a way that functions or data structures alone cannot be This single feature is what sets C++ apart from C At the same time, the C ancestry is not discarded C++ still benefits from the speed advantages of C, offering much better performance than other object-oriented languages Using only functions and data structures, as covered in the previous two lessons, is known as procedural programming Object-oriented programming uses a new paradigm, and as such provides a different way to approach programming Some advocates of object-orient programming often disparage procedural programming, and insist that programmers give themselves over to this new paradigm completely They are critical of programmers who mix paradigms (or, worse yet, use C++, but not use objects), and they are supporters of “pure” object-oriented languages such as Java The implication is that object-oriented programming is so radically different from procedural programming that everything that you know about procedural techniques should be discarded, and that every feature in every program should be represented as an object or set of objects But object-oriented programming does not and cannot entirely replace procedural programming The object-oriented purists seem to forget that objects use functions Procedural issues such as function calls, arguments, return values, automatic variables, and data types apply to object-oriented programming as much as they to procedural programming Object-oriented techniques don’t replace procedural techniques; they augment and enhance them We didn’t spend the first two lessons studying procedural programming features just for historical reasons Nor did we it to demonstrate how horrible these concepts are in contrast to object-oriented features We need the foundation that procedural programming provides in order to be effective objectoriented programmers We won’t discard what we’ve learned thus far; we’ll use it to create objects Having said that, good object oriented programming does require a different mindset For programmers with extensive procedural experience in particular, this can be a rough transition, because they have to break learned habits It is not uncommon for these programmers to be very slow to fully adopt objectoriented approaches, which results in code that uses a blend of techniques In this sense, new programmers who start with object-oriented languages have an advantage They learn to use objects from the start, and don’t have to fight established thought patterns Lesson – Classes C++ uses the term class to describe an object definition A class is a data type, just like an intrinsic data type or a structure As a data type, classes are blueprints that describe data entities, and can be used to declare variables Instead of being called variables, however, instances of a class are called objects Before we can create an object, therefore, we must define the class that describes it One way to create a class is to use the class keyword, like this: class Player { www.gameinstitute.com Introduction to C and C++ : Week 3: Page of 47 char name[80]; int score; }; This definition creates a class called Player The Player class defines two members, name, and score, which can be used to store data about the player In object-oriented lingo, these data definitions are called data members In this case the Player class is said to have two data members The Player class shown above looks almost exactly like the Player structure from Lesson The only difference is the class keyword (the Player structure uses the struct keyword) In fact, there is virtually no difference between a class and a structure Consider these two definitions: class Player { char name[80]; int score; }; struct Player { char name[80]; int score; }; Both of these definitions define a data type called Player Both of these types can be used to create variables, and both contain the same data members In C++, a class and a structure are virtually identical The only difference is the default permissions By default, the struct keyword results in a data type whose members are public, whereas the members of a class are private We know that the contents of the struct are freely accessible (public) because we assigned and inspected them freely in Lesson A class, however, because it is private by default, does not allow access to its data members: class Player { char name[80]; int score; }; Player player; player.score = 0; // compiler error! ‘score’ is private What is the point of a data structure with elements that can’t be accessed? We’ll get to that soon First, let’s talk about how we can control the accessibility of data members Access to data members is controlled with the C++ public and private keywords Using the public keyword makes everything that follows it freely accessible Likewise, the private keyword restricts access items that follow it Using the public keyword, we can rewrite the class-based version of Player like this: class Player { public: www.gameinstitute.com Introduction to C and C++ : Week 3: Page of 47 char name[80]; int score; }; This version of Player provides full access to its data members because they are explicitly declared to be public The default private permission level of the class keyword is overridden by the use of the public keyword This version of Player behaves exactly like the Player structure used in Lesson The default permission level of struct can be overridden with the private keyword: struct Player { private: char name[80]; int score; }; This version is effectively the same as the original class-based version; its data members are not publicly accessible The permission level of each data member can be controlled separately: struct Player { char name[80]; private: int score; }; In this case, name is publicly accessible (because struct is used), but score is not The effect of public and private takes effect for all of the members that follow, until either the end of the definition is reached (the closing curly brace), or another permissions modifier is encountered There is no limit to the number of permission modifiers that can be used, and there is no rule against redundant modifiers (usages of public or private that not change the current permission setting, because the specified permission is already in effect.) When used to modify the access permissions for members, both keywords must be followed by a colon Despite the fact that there is very little difference between class and struct, the term structure is used to refer to data types that contain only public data members, and the term class is used to refer to types that contain a combination of data types and member functions We’ll talk about member functions next Member Functions The goal of object-oriented programming is to create objects that are safe, robust, and easy to use Although public data members are easy to use, they provide a level of access that is discouraged because they allow anyone to assign any value to any data member As a result, data members should almost always be private Instead of direct access, data members are usually accessed via member functions, like this: class Player { public: void SetScore(int s); www.gameinstitute.com Introduction to C and C++ : Week 3: Page of 47 int GetScore(); private: char name[80]; int score; }; By adding member functions, we now have access—albeit indirect access—to data members that are private The SetScore function provides a way to assign score, and GetScore provides support for score retrieval (for now we’re providing member functions for only the score data member.) A member function is just like a regular function except that it belongs to a class It is declared within the class definition, and, as a member of that class, is given access to all data members and member functions present in the class, private or otherwise Member functions can’t be used outside of the context of the class The member functions that are now part of the Player class can’t be called without a Player object (a variable of the Player type.) This in is contrast to the functions we’ve been using thus far, that are not part of a class Non-class member functions are called C functions, or global functions In the example above, the body of the GetScore and SetScore member functions are not provided, so this class is incomplete The class has been defined, and the two member functions have been declared, but they have not been defined We can complete the class by providing the member function bodies inside the class, like this: class Player { public: void SetScore(int s) int GetScore() private: char name[80]; int score; }; { score = s; } { return score; } Notice that the semicolon following each member function in the previous example has been replaced with the function body for each member function The SetScore function body uses the s parameter to assign the private data member score, and the GetScore function simply returns the value of score As we discussed in Lesson 1, it is typical to begin function bodies with an opening curly bracket on the line following the function name, but simple member functions that are defined within a class definition are generally an exception to this rule The formatting shown above is typical for simple accessor functions—member functions that merely provided access to a data member Alternatively, member functions can be defined outside the class definition, like this: class Player { public: void SetScore(int s); int GetScore(); private: char name[80]; int score; }; www.gameinstitute.com Introduction to C and C++ : Week 3: Page of 47 void Player::SetScore(int s) { score = s; } int Player::GetScore() { return s; } Although this syntax is not normally used for simple accessor functions like SetScore and GetScore, it is common for functions with more complex function bodies, and for member functions whose class is defined in a header file (we’ll talk about that in Lesson 4) Member functions that are defined outside the class definition must use the C++ scope resolution operator, which is two colons (::) The name that appears to the left of this operator is the class to which the member function belongs, and the name to the right is the member function name Defining member function outside the class definition is exactly like defining a global function, except that the scope resolution operator is used to indicate the class or scope to which the function belongs If no scope resolution operator were used in the definitions above, the compiler would have no way to know that GetScore and SetScore belong to the Player class This would cause a compiler error because both functions refer to score, which doesn’t exist outside the Player class Providing member functions is safer than allowing direct access to data members because you—as the author of a class—can dictate what data values are allowed for each data member For example, suppose that the game you are writing prohibits the use of negative scores If the score data member were public, it could be assigned negative values from anywhere in the game code But because we’ve made it private, we’re requiring that it be set via the SetScore member function This gives us the opportunity to enforce restrictions on values passed to SetScore One strategy would be to implement SetScore like this: void Player::SetScore(int s) { if (s >= 0) { score = s; } } This prevents score from being set to a negative value If a negative value is provided to SetScore, it is simply ignored This is less than ideal, however, because if a negative value is passed to SetScore, no indication is given that the function call had no effect We could change the return type of SetScore to bool, and return true if a valid score is provided, and false otherwise, but this means that the caller is expected to check the return value of SetScore each time it is called Return values are easy to ignore— there is no way to insure that they are checked, or even retrieved A better solution is to use an assert An assert is a macro that can be used to force specific conditions within your code If the condition passed to assert is non-zero (true), assert has no effect But if the condition resolves to (false), assert halts the executable, and displays the name of the source file and the line where the assertion failed Using assert requires that the assert.h header file be included, and looks like this when used with the SetScore function: void Player::SetScore(int s) www.gameinstitute.com Introduction to C and C++ : Week 3: Page of 47 { assert( s >= ); score = s; } This code dictates that values sent to SetScore must be non-negative If not, execution will halt, and you’ll be informed the exact location of the offending code The neat thing about assert is that, as a macro, it is defined in such a way that it has absolutely no effect in release builds As a result, assert can be used liberally, and they’ll announce problems with the code in Debug builds, but the release build will run optimally Before we continue, let’s provide member functions for the name data member in the Player class The first step is to declare these functions within the Player class, granting them public access: class Player { public: void SetScore(int s); int GetScore(); void SetName(char* n); char* GetName(); private: char name[80]; int score; }; The SetName member function takes a char pointer as an argument This will allow callers to provide the address of the player name Likewise the GetName function returns a pointer to the name, and not the data itself SetName looks like this: void Player::SetName(char* n) { assert( n ); strcpy( name, n ); } This implementation of SetName uses the provided pointer to copy the contents of the name data member Notice that an assert is used to mandate that n is non-zero When dealing with pointers, it’s important to account for the possibility that null pointers will be provided In this case providing a nullpointer is not allowed, as the SetName function needs the address of a valid string from which to copy the player name Now we’re ready to write the GetName function This function returns a pointer to a char, so we can write it like this: char* Player::GetName() { return name; } www.gameinstitute.com Introduction to C and C++ : Week 3: Page of 47 GetName simply returns the address of the name array No address of operator is required, because name is an array, and therefore a pointer This allows us to provide the player name to the calling function without making a copy of the data The GetName function can be used like this: Player localPlayer; // global object, initialized with a player name elsewhere void DisplayLocalPlayerName() { char* pName = localPlayer.GetName(); // (code to display the name on the screen) } This code works fine, but only if the calling code uses the returned pointer for read-only purposes There is nothing preventing the code from assigning the content of our private data member, so an errant programmer on the team might this: Player localPlayer; // global object, initialized with a player name elsewhere void DisplayLocalPlayerName() { char* pName = localPlayer.GetName(); strcpy( pName, “surprise!” ); // (code to display the name on the screen) } This code, using the pointer that we’ve provided, writes a new string to a private data member The rogue programmer has overwritten the player name with the string “surprise!” This constitutes a breech in the security of our class because our intention was that the only way to assign the name data member was with the SetName member function Luckily, this problem can be avoided with the const keyword C++ provides the const keyword to allow the declaration of variables that are “read-only.” Const variables are just like regular variables, but they cannot be modified The only way to assign a value to a const variable is to initialize it during declaration Here’s an example: const int MaxPlayers = 10; cout

Ngày đăng: 19/01/2014, 02:20

Từ khóa liên quan

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan