CS计算机代考程序代写 compiler c++ 2021/8/8 Encapsulation | Member Operators

2021/8/8 Encapsulation | Member Operators

https://ict.senecacollege.ca/~oop244/pages/content/overl.html 1/11

ICT Home Outline Timeline Notes IPC Notes MySeneca Workshops Assignments Instructor

Software
Development

OOP244

Part C – Encapsulation

Member Operators

Overload operators to form expressions involving objects of a class
Describe the syntax for overloading operators that are members of a class

Describe casting and conversion operations on instances of a class

“Programmers hate surprises: Overload operators only for good reason, and preserve natural semantics; if that’s difficult, you
might be misusing operator overloading” (Sutter, Alexandrescu, 2005)

Operations | Binary | Unary | Conversions | Casts | Temporaries | Summary | Exercise

An important feature of object-oriented programs is support for expressions composed of objects. An expression consists of an
operator and a set of operands. The expression evaluates to a value of specific type. In languages like C++, all operators are built-
in. The core language defines the logic for the operands of fundamental type. To support expressions with operands of class type,
we need to overload the built-in operators for those operands. Overloading an operator entails declaring a corresponding function
in the class definition and defining its logic in the implementation file.

This chapter lists the C++ operators that we may overload and describes the syntax for overloading operators using member
functions. These functions cover unary and binary operations on the current object. This chapter also describes how to define
casting operations and how to use temporary object effectively.

OPERATIONS

In the C++ language, the keyword operator identifies an overloaded operation. We follow the keyword by the operator’s
symbol. The signature of a member function that overloads an operator consists of the keyword, the symbol and the type of the
right operand, if any, within parentheses. The left operand of any member operator is the current object.

For example, an overloaded assignment operator for a Student right operand takes the form

Student& operator=(const Student&);

Candidates for Overloading

C++ lets us overload the following operators (amongst others):

binary arithmetic (+ – * / %)
assignment – simple and compound (= += -= *= /= %=)
unary – pre-fix post-fix plus minus (++ — + -)
relational (== < > <= >= !=)
logical (&& || !)
insertion, extraction (<< >>)

C++ DOES NOT ALLOW overloading of the following operators (amongst others):

the scope resolution operator (::)
the member selection operator (.)
the member selection through pointer to member operator (.*)
the conditional operator (?:)

C++ DOES NOT let us introduce or define new operators.

Classifying Operators

Welcome

Notes

Welcome to OO

Object Terminology

Modular Programming

Types Overloading

Dynamic Memory

Member Functions

Construction

Current Object

Member Operators

Class + Resources

Helper Functions

Input Output

Derived Classes

Derived Functions

Virtual Functions

Abstract Classes

Templates

Polymorphism

I/O Refinements

D C + Resources

Standards

Bibliography

Library Functions

ASCII Sequence

Operator Precedence

C++ and C

Workshops

Assignments

Handouts

Practice

Resources

https://ict.senecac.on.ca/
https://ict.senecacollege.ca/~oop244/index.html
http://www.senecacollege.ca/ssos/findwithoutsemester/oop244/sict
https://ict.senecacollege.ca/~oop244/pages/timeline.html
https://ict.senecacollege.ca/~oop244/pages/content/index.html
https://scs.senecac.on.ca/~ipc144/pages/content/index.html
https://my.senecacollege.ca/webapps/portal/frameset.jsp
https://ict.senecacollege.ca/~oop244/dynamic/workshops/index.html
https://ict.senecacollege.ca/~oop244/pages/assignments/index.html
https://ict.senecacollege.ca/~oop244/pages/instructors/index.html
https://ict.senecacollege.ca/~oop244/pages/content/overl_p.html
https://ict.senecacollege.ca/~oop244/pages/welco.html
https://ict.senecacollege.ca/~oop244/pages/content/index.html
https://ict.senecacollege.ca/~oop244/pages/content/langu.html
https://ict.senecacollege.ca/~oop244/pages/content/objec.html
https://ict.senecacollege.ca/~oop244/pages/content/compi.html
https://ict.senecacollege.ca/~oop244/pages/content/rudim.html
https://ict.senecacollege.ca/~oop244/pages/content/dynam.html
https://ict.senecacollege.ca/~oop244/pages/content/cppst.html
https://ict.senecacollege.ca/~oop244/pages/content/ctors.html
https://ict.senecacollege.ca/~oop244/pages/content/membe.html
https://ict.senecacollege.ca/~oop244/pages/content/overl.html
https://ict.senecacollege.ca/~oop244/pages/content/deepc.html
https://ict.senecacollege.ca/~oop244/pages/content/nonme.html
https://ict.senecacollege.ca/~oop244/pages/content/custo.html
https://ict.senecacollege.ca/~oop244/pages/content/inher.html
https://ict.senecacollege.ca/~oop244/pages/content/dfunc.html
https://ict.senecacollege.ca/~oop244/pages/content/inclu.html
https://ict.senecacollege.ca/~oop244/pages/content/abstr.html
https://ict.senecacollege.ca/~oop244/pages/content/param.html
https://ict.senecacollege.ca/~oop244/pages/content/adhoc.html
https://ict.senecacollege.ca/~oop244/pages/content/basic.html
https://ict.senecacollege.ca/~oop244/pages/content/dclas.html
https://ict.senecacollege.ca/~oop244/pages/content/ansis.html
https://ict.senecacollege.ca/~oop244/pages/content/bibli.html
https://ict.senecacollege.ca/~oop244/pages/content/libraries.html
https://ict.senecacollege.ca/~oop244/pages/content/ascii.html
https://ict.senecacollege.ca/~oop244/pages/content/prece.html
https://ict.senecacollege.ca/~oop244/pages/content/c_cpp.html
https://ict.senecacollege.ca/~oop244/dynamic/workshops/index.html
https://ict.senecacollege.ca/~oop244/pages/assignments/index.html
https://ict.senecacollege.ca/~oop244/pages/handouts/index.html
https://ict.senecacollege.ca/~oop244/pages/practice/index.html
https://ict.senecacollege.ca/~oop244/pages/resources/index.html

2021/8/8 Encapsulation | Member Operators

https://ict.senecacollege.ca/~oop244/pages/content/overl.html 2/11

We classify operators by the number of operands that they take:

unary – one operand – post-fix increment/decrement, pre-fix increment/decrement, pre-fix plus, pre-fix minus
binary – two operand – assignment, compound assignment, arithmetic, relational, logical
ternary – three operands – conditional operator

Members and Helpers

We overload operators in either of two ways, as:

member operators – part of the class definition with direct access to the class representation
helper operators – supporting the class, without direct access to its representation

We prefer to declare operators that change the state of their left operand as member operators. Helper operators are described
separately in the chapter entitled Helper Functions.

Overloading a Member Operator

Signature

The signature of an overloaded member operator consists of:

the operator keyword
the operation symbol
the type of its right operand, if any
the const status of the operation

The compiler binds an expression to the member function with the signature that matches the operator symbol, the operand type
and the const status.

Promotion or Narrowing of Arguments

If the compiler cannot find an exact match to an operation’s signature, the compiler will attempt a rather complicated selection
process to find an optimal fit, promoting or narrowing the operand value into a related type if necessary.

Type of the Evaluated Expression

The return type of the member function declaration identifies the type of the evaluated expression.

Good Design Practice

Programmers expect an operator to perform its operation in a way similar if not identical to the way that the operator performs its
operation on any fundamental type as defined by the core language. For instance, + implies addition of two values in a binary
operation (not subtraction). In defining a member operator we code its logic to be consistent with operations on other types.

BINARY OPERATORS

A binary operation consists of one operator and two operands. In a binary member operator, the left operand is the current object
and the member function takes one explicit parameter: the right operand.

The declaration of a binary member operator takes the form

return_type operator symbol (type [identifier])

return_type is the type of the evaluated expression. operator identifies the function as an operation. symbol specifies
the kind of operation. type is the type of the right operand. identifier is the right operand’s name.

Example

Let us overload the += operator for a float as the right operand, in order to add a single grade to a Student object:

// Overloading Operators
// operators.cpp

#include
using namespace std;
const int NG = 20;

class Student {
int no;

https://ict.senecacollege.ca/~oop244/pages/content/nonme.html

2021/8/8 Encapsulation | Member Operators

https://ict.senecacollege.ca/~oop244/pages/content/overl.html 3/11

float grade[NG];
int ng;
void set(int, const float*, int);
public:
Student();
Student(int, const float*, int);
void display() const;
Student& operator+=(float g);
};

Student::Student() {
no = 0;
ng = 0;
}

Student::Student(int sn, const float* g, int ng_) {
set(sn, g, ng_);
}

void Student::set(int sn, const float* g, int ng_) {
bool valid = sn > 0 && g != nullptr && ng_ >= 0;
if (valid)
for (int i = 0; i < ng_ && valid; i++) valid = g[i] >= 0.0f && g[i] <= 100.0f; if (valid) { // accept the client's data no = sn; ng = ng_ < NG ? ng_ : NG; for (int i = 0; i < ng; i++) grade[i] = g[i]; } else { no = 0; ng = 0; } } void Student::display() const { if (no > 0) {
cout << no << ":\n"; cout.setf(ios::fixed); cout.precision(2); for (int i = 0; i < ng; i++) { cout.width(6); cout << grade[i] << endl; } cout.unsetf(ios::fixed); cout.precision(6); } else { cout << "no data available" << endl; } } Student& Student::operator+=(float g) { if (no != 0 && ng < NG && g >= 0.f && g <= 100.f) grade[ng++] = g; return *this; } int main () { float gh[] = {89.4f, 67.8f, 45.5f}; Student harry(1234, gh, 3); harry.display(); harry += 78.23f; harry.display(); } 1234: 89.40 67.80 45.50 1234: 89.40 67.80 45.50 78.23 UNARY OPERATORS A unary operation consists of one operator and one operand. The left operand of a unary member operator is the current object. The operator does not take any explicit parameters (with one exception - see post-fix operators below). 2021/8/8 Encapsulation | Member Operators https://ict.senecacollege.ca/~oop244/pages/content/overl.html 4/11 The header for a unary member operator takes the form return_type operator symbol() return_type is the type of the evaluated expression. operator identifies an operation. symbol identifies the kind of operation. Pre-Fix Operators We overload the pre-fix increment/decrement operators to increment/decrement the current object and return a reference to its updated value. The header for a pre-fix operator takes the form Type& operator++() or Type& operator--() Example Let us overload the pre-fix increment operator for our Student class so that a pre-fix expression increases all of the Student's grades by one mark, if possible: // Pre-Fix Operators // preFixOps.cpp #include
using namespace std;
const int NG = 20;

class Student {
int no;
float grade[NG];
int ng;
void set(int, const float*, int);
public:
Student();
Student(int, const float*, int);
void display() const;
Student& operator++();
};

Student::Student() {
no = 0;
ng = 0;
}

Student::Student(int sn, const float* g, int ng_) {
set(sn, g, ng_);
}

void Student::set(int sn, const float* g, int ng_) {
bool valid = sn > 0 && g != nullptr && ng_ >= 0;
if (valid)
for (int i = 0; i < ng_ && valid; i++) valid = g[i] >= 0.0f && g[i] <= 100.0f; if (valid) { // accept the client's data no = sn; ng = ng_ < NG ? ng_ : NG; for (int i = 0; i < ng; i++) grade[i] = g[i]; } else { no = 0; ng = 0; } } void Student::display() const { if (no > 0) {
cout << no << ":\n"; cout.setf(ios::fixed); cout.precision(2); for (int i = 0; i < ng; i++) { cout.width(6); cout << grade[i] << endl; 2021/8/8 Encapsulation | Member Operators https://ict.senecacollege.ca/~oop244/pages/content/overl.html 5/11 } cout.unsetf(ios::fixed); cout.precision(6); } else { cout << "no data available" << endl; } } Student& Student::operator++() { for (int i = 0; i < ng; i++) if (grade[i] < 99.0f) grade[i] += 1.f; return *this; } int main () { float gh[] = {89.4f, 67.8f, 45.5f}; Student harry(1234, gh, 3), backup; harry.display(); backup = ++harry; harry.display(); backup.display(); } 1234: 89.40 67.80 45.50 1234: 90.40 68.80 46.50 1234: 90.40 68.80 46.50 Post-Fix Operators We overload the post-fix operators to increment/decrement the current object after returning its value. The header for a post-fix operator takes the form return_type operator++(int) or Type operator--(int) The int type in the header distinguishes the post-fix operators from their pre-fix counterparts. Example Let us overload the incrementing post-fix operator for our Student class so that a post-fix expression increases all of the Student's grades by one mark, if possible: // Post-Fix Operators // postFixOps.cpp #include
using namespace std;
const int NG = 20;

class Student {
int no;
float grade[NG];
int ng;
void set(int, const float*, int);
public:
Student();
Student(int, const float*, int);
void display() const;
Student& operator++();
Student operator++(int);
};

Student::Student() {
no = 0;
ng = 0;
}

Student::Student(int sn, const float* g, int ng_) {
set(sn, g, ng_);
}

void Student::set(int sn, const float* g, int ng_) {
bool valid = sn > 0 && g != nullptr && ng_ >= 0;
if (valid)
for (int i = 0; i < ng_ && valid; i++) valid = g[i] >= 0.0f && g[i] <= 100.0f; 2021/8/8 Encapsulation | Member Operators https://ict.senecacollege.ca/~oop244/pages/content/overl.html 6/11 if (valid) { // accept the client's data no = sn; ng = ng_ < NG ? ng_ : NG; for (int i = 0; i < ng; i++) grade[i] = g[i]; } else { no = 0; ng = 0; } } void Student::display() const { if (no > 0) {
cout << no << ":\n"; cout.setf(ios::fixed); cout.precision(2); for (int i = 0; i < ng; i++) { cout.width(6); cout << grade[i] << endl; } cout.unsetf(ios::fixed); cout.precision(6); } else { cout << "no data available" << endl; } } Student& Student::operator++() { for (int i = 0; i < ng; i++) if (grade[i] < 99.0f) grade[i] += 1.f; return *this; } Student Student::operator++(int) { Student s = *this; // save the original ++(*this); // call the pre-fix operator return s; // return the original } int main () { float gh[] = {89.4f, 67.8f, 45.5f}; Student harry(1234, gh, 3), backup; harry.display(); backup = harry++; harry.display(); backup.display(); } 1234: 89.40 67.80 45.50 1234: 90.40 68.80 46.50 1234: 89.40 67.80 45.50 We avoid duplicating logic by calling the pre-fix operator from the post-fix operator. Return Types The return types of the pre-fix and post-fix operators differ. The post-fix operator returns a copy of the current object as it was before any changes took effect. The pre-fix operator returns a reference to the current object, which accesses the data after the changes have taken effect. TYPE CONVERSION OPERATORS Type conversion operators define implicit conversions to different types, including fundamental types. For the following code to compile, the compiler needs information on how to convert a Student object to a bool value: Student harry; if (harry) harry.display(); 2021/8/8 Encapsulation | Member Operators https://ict.senecacollege.ca/~oop244/pages/content/overl.html 7/11 bool operator Let us define a conversion operator that returns true if the Student object has valid data and false if the object is in a safe empty state. We add the following declaration to the class definition: const int NG = 20; class Student { int no; float grade[NG]; int ng; void set(int, const float*, int); public: Student(); Student(int, const float*, int); void display() const; operator bool() const; }; We define the conversion operator in the implementation file #include "Student.h" // ... Student::operator bool() const { return no != 0; } Good Design Tip Conversion operators easily lead to ambiguities. Good design uses them quite sparingly and keeps their implementations trivial. CAST OPERATOR C++ defines the casting operation for a class type in terms of a single-argument constructor. This overloaded constructor defines the rule for casting a value of its parameter type to the class type, as well as constructing an object from an argument of the parameter type. The following program demonstrates both uses of a single-argument constructor on an int argument: // Casting // casting.cpp #include
using namespace std;
const int NG = 20;

class Student {
int no;
float grade[NG];
int ng;
void set(int, const float*, int);
public:
Student();
Student(int);
Student(int, const float*, int);
void display() const;
};

Student::Student() {
no = 0;
ng = 0;
}

Student::Student(int sn) {

2021/8/8 Encapsulation | Member Operators

https://ict.senecacollege.ca/~oop244/pages/content/overl.html 8/11

float g[] = {0.0f};
set(sn, g, 0);
}

Student::Student(int sn, const float* g, int ng_) {
set(sn, g, ng_);
}

void Student::set(int sn, const float* g, int ng_) {
bool valid = sn > 0 && g != nullptr && ng_ >= 0;
if (valid)
for (int i = 0; i < ng_ && valid; i++) valid = g[i] >= 0.0f && g[i] <= 100.0f; if (valid) { // accept the client's data no = sn; ng = ng_ < NG ? ng_ : NG; for (int i = 0; i < ng; i++) grade[i] = g[i]; } else { no = 0; ng = 0; } } void Student::display() const { if (no > 0) {
cout << no << ":\n"; cout.setf(ios::fixed); cout.precision(2); for (int i = 0; i < ng; i++) { cout.width(6); cout << grade[i] << endl; } cout.unsetf(ios::fixed); cout.precision(6); } else { cout << "no data available" << endl; } } int main () { Student harry(975), nancy; harry.display(); nancy = (Student)428; nancy.display(); } 975: 428: The first use converts 975 to the Student object harry. The second use casts 428 to a Student object containing the number 428. Both objects hold empty grade lists. Promotion (Optional) For the same result as the above cast, we may omit the cast operator and defer to the compiler promoting the int value 428 to a Student object before assigning the object to nancy: int main () { Student harry(975), nancy; harry.display(); cout << endl; nancy = 428; // promotes an int to a Student nancy.display(); cout << endl; } 975 428 The compiler inserts code that creates a temporary Student object using the single-argument constructor. The constructor receives the value 428 and initializes no to 428 and ng to 0. Then, the assignment operator copies the temporary object to nancy. Finally, the compiler inserts code that destroys the temporary object removing it from memory. 2021/8/8 Encapsulation | Member Operators https://ict.senecacollege.ca/~oop244/pages/content/overl.html 9/11 Explicit (Optional) Declaring several single-argument constructors raise the possibility of potential ambiguities in automatic conversions form one type to another. Limiting the number of single-argument constructors in a class definition helps avoid such potential ambiguities. To prohibit the compiler from using a single-argument constructor for any implicit conversion, we declare that constructor explicit: class Student { int no; char grade[M+1]; void set(int, const float*, int); public: Student(); explicit Student(int); Student(int, const float*, int); void display() const; }; With such a declaration, the second invocation in the example at the start of the section above (nancy = 428) would generate a compiler error. TEMPORARY OBJECTS C++ compilers create temporary objects in a variety of situations. A temporary object has no name and is destroyed as the last step in evaluating the expression that contains its creation point. Consider the assignment expression below: int main () { Student harry(975), nancy; harry.display(); nancy = Student(428); // temporary Student object nancy.display(); } 975: 428: Localizing Constructor Logic We can use temporary objects to access validation logic localized within one constructor. Note the temporary object assignments to the current object (*this) in the one-argument and three-argument constructors below: // Localized Validation // localize.cpp #include
using namespace std;
const int NG = 20;

class Student {
int no;
float grade[NG];
int ng;
public:
Student();
Student(int);
Student(int, const float*, int);
void display() const;
};

Student::Student() {
// safe empty state
no = 0;
ng = 0;
}

Student::Student(int sn) {

2021/8/8 Encapsulation | Member Operators

https://ict.senecacollege.ca/~oop244/pages/content/overl.html 10/11

float g[] = {0.0f};
*this = Student(sn, g, 0);
}

Student::Student(int sn, const float* g, int ng_) {
bool valid = sn > 0 && g != nullptr && ng_ >= 0;
if (valid)
for (int i = 0; i < ng_ && valid; i++) valid = g[i] >= 0.0f && g[i] <= 100.0f; if (valid) { // accept the client's data no = sn; ng = ng_ < NG ? ng_ : NG; for (int i = 0; i < ng; i++) grade[i] = g[i]; } else { *this = Student(); } } void Student::display() const { if (no > 0) {
cout << no << ":\n"; cout.setf(ios::fixed); cout.precision(2); for (int i = 0; i < ng; i++) { cout.width(6); cout << grade[i] << endl; } cout.unsetf(ios::fixed); cout.precision(6); } else { cout << "no data available" << endl; } } int main () { float gh[] = {89.4f, 67.8f, 45.5f}; Student harry(1234, gh, 3), josee(1235), empty; harry.display(); josee.display(); empty.display(); } 1234: 89.40 67.80 45.50 1235: no data available The three-argument constructor validates all data received from client code. If the validation fails, this constructor creates a temporary object in a safe empty state and assigns that temporary object to the current object. Note that the single-argument constructor uses the temporary object created by the three-argument constructor to initialize the current object. Good Design Tip Using temporary objects to avoid repeated logic is good programming practice. If we update the logic later, there is no chance that we will update the logic in one part of the source code and neglect to update identical logic in another part of the code. SUMMARY C++ allows overloading of most of the operators for operands of class type we cannot define new operators or redefine operations on the fundamental types the keyword operator followed by a symbol identifies an operation the left operand in an overloaded member operator is the current object we use member operators to overload operations that modify the left operand the int keyword in the signature for increment/decrement operator identifies the post-fix operation distinguishing it from the pre-fix operation we use temporary objects to localize logic, which improves maintainability EXERCISE Complete the Handout on Member Operators https://ict.senecacollege.ca/~oop244/pages/handouts/h8.html 2021/8/8 Encapsulation | Member Operators https://ict.senecacollege.ca/~oop244/pages/content/overl.html 11/11 print this page Top Previous: The Current Object Next: Classes and Resources ICT Home Outline Timeline Notes IPC Notes MySeneca Workshops Assignments Instructor Designed by Chris Szalwinski Copying From This Site Last Modified: 02/03/2019 14:54 https://ict.senecacollege.ca/~oop244/pages/content/overl_p.html https://ict.senecacollege.ca/~oop244/pages/content/membe.html https://ict.senecacollege.ca/~oop244/pages/content/deepc.html https://ict.senecac.on.ca/ https://ict.senecacollege.ca/~oop244/index.html http://www.senecacollege.ca/ssos/findwithoutsemester/oop244/sict https://ict.senecacollege.ca/~oop244/pages/timeline.html https://ict.senecacollege.ca/~oop244/pages/content/index.html https://scs.senecac.on.ca/~ipc144/pages/content/index.html https://my.senecacollege.ca/webapps/portal/frameset.jsp https://ict.senecacollege.ca/~oop244/dynamic/workshops/index.html https://ict.senecacollege.ca/~oop244/pages/assignments/index.html https://ict.senecacollege.ca/~oop244/pages/instructors/index.html https://ict.senecacollege.ca/~oop244/pages/copyright.html http://creativecommons.org/licenses/by/2.5/ca/