Friendship and Encapsulation in C++



Similar documents
3. Mathematical Induction

Managing Variability in Software Architectures 1 Felix Bachmann*

A deeper look at Inline functions

Object Oriented Design

Parameter Passing in Pascal

Introduction to C++ Introduction to C++ Week 7 Dr Alex Martin 2013 Slide 1

Limitations of Data Encapsulation and Abstract Data Types

INTERNATIONAL FRAMEWORK FOR ASSURANCE ENGAGEMENTS CONTENTS

Fourth generation techniques (4GT)

(Refer Slide Time: 2:03)

Regions in a circle. 7 points 57 regions

Polynomial and Rational Functions

Quick Preview PROPERTY DAMAGE

Chapter 5 Names, Bindings, Type Checking, and Scopes

Moving from CS 61A Scheme to CS 61B Java

C++ INTERVIEW QUESTIONS

Lesson 4 Web Service Interface Definition (Part I)

How To Teach Object Oriented Programming At An Introductory Level Course

Johannes Sametinger. C. Doppler Laboratory for Software Engineering Johannes Kepler University of Linz A-4040 Linz, Austria

Writing Thesis Defense Papers

THE IMPACT OF INHERITANCE ON SECURITY IN OBJECT-ORIENTED DATABASE SYSTEMS

Partial Fractions. Combining fractions over a common denominator is a familiar operation from algebra:

Spiel. Connect to people by sharing stories through your favorite discoveries

CS104: Data Structures and Object-Oriented Design (Fall 2013) October 24, 2013: Priority Queues Scribes: CS 104 Teaching Team

Component Based Software Engineering: A Broad Based Model is Needed

SOACertifiedProfessional.Braindumps.S90-03A.v by.JANET.100q. Exam Code: S90-03A. Exam Name: SOA Design & Architecture

Reflections on Probability vs Nonprobability Sampling

Architecture Description of <Architecture Name> for <System of Interest>

The «include» and «extend» Relationships in Use Case Models

Symbol Tables. Introduction

Introduction to Software Paradigms & Procedural Programming Paradigm

There is a simple equation for calculating dilutions. It is also easy to present the logic of the equation.

Description of Class Mutation Mutation Operators for Java

recursion, O(n), linked lists 6/14

Copyright 2001, Bill Trudell. Permission is granted to copy for the PLoP 2001 conference. All other rights reserved.

Chapter 7: Additional Topics

Qualitative Interview Design: A Practical Guide for Novice Investigators

Object Oriented Software Design II

They Say, I Say: The Moves That Matter in Academic Writing

Pluggable Type Systems. Gilad Bracha

[Refer Slide Time: 05:10]

Introducing Formal Methods. Software Engineering and Formal Methods

2) Write in detail the issues in the design of code generator.

How To Draw A Cell Phone Into A Cellphone In Unminimal Diagram (Uml)

Chapter 4 OOPS WITH C++ Sahaj Computer Solutions

Evaluating a new programming language

Syntax Check of Embedded SQL in C++ with Proto

CS107L Handout 04 Autumn 2007 October 19, 2007 Custom STL-Like Containers and Iterators

Using Provenance to Improve Workflow Design

LITERATURE REVIEWS. The 2 stages of a literature review

COMPARISON OF OBJECT-ORIENTED AND PROCEDURE-BASED COMPUTER LANGUAGES: CASE STUDY OF C++ PROGRAMMING

Unleashing Hidden Powers of Inventor with the API Part 1. Getting Started with Inventor VBA Hello Inventor!

UML MODELLING OF DIGITAL FORENSIC PROCESS MODELS (DFPMs)

Software Engineering. Software Engineering. Component-Based. Based on Software Engineering, 7 th Edition by Ian Sommerville

IF The customer should receive priority service THEN Call within 4 hours PCAI 16.4

Lecture Notes on Binary Search Trees

The Software Process. The Unified Process (Cont.) The Unified Process (Cont.)

Chapter 8: Bags and Sets

Software Service Engineering Architect s Dream or Developer s Nightmare?

The Clean programming language. Group 25, Jingui Li, Daren Tuzi

Internationalization and Web Services

Using Use Cases for requirements capture. Pete McBreen McBreen.Consulting

Please find below some views on the issues raised by the EMA Senior Medical Officer's Reflections on the legal basis for EMA/CHMP opinions.

Pigeonhole Principle Solutions

Discrete Mathematics and Probability Theory Fall 2009 Satish Rao, David Tse Note 2

Functional Decomposition Top-Down Development

Liquid Democracy versus Direct Democracy through Initiative and Referendum: Which Is Best?

The Java Series. Java Essentials I What is Java? Basic Language Constructs. Java Essentials I. What is Java?. Basic Language Constructs Slide 1

6.3 Conditional Probability and Independence

Operator Overloading. Lecture 8. Operator Overloading. Running Example: Complex Numbers. Syntax. What can be overloaded. Syntax -- First Example

language 1 (source) compiler language 2 (target) Figure 1: Compiling a program

Module 2. Software Life Cycle Model. Version 2 CSE IIT, Kharagpur

Cryptography and Network Security Prof. D. Mukhopadhyay Department of Computer Science and Engineering Indian Institute of Technology, Kharagpur

Publishing papers in international journals

Recursive Implementation of Recursive Data Structures

The C Programming Language course syllabus associate level

Secrets of printf. 1 Background. 2 Simple Printing. Professor Don Colton. Brigham Young University Hawaii. 2.1 Naturally Special Characters

Appendix B Data Quality Dimensions

Module 1. Introduction to Software Engineering. Version 2 CSE IIT, Kharagpur

Writing an essay. This seems obvious - but it is surprising how many people don't really do this.

REMOTE DEVELOPMENT OPTION

Unified Call Syntax: x.f(y) and f(x,y)

This puzzle is based on the following anecdote concerning a Hungarian sociologist and his observations of circles of friends among children.

Domain of a Composition

1. Polymorphism in C++...2

PERCEPTION OF BASIS OF SHE AND SHE RISK MANAGEMENT

Focus on Essay Writing

RARP: Reverse Address Resolution Protocol

UML for C# Modeling Basics

3. Logical Reasoning in Mathematics

Object-oriented design methodologies

Transcription:

Friendship and Encapsulation in C++ Adrian P Robson Department of Computing University of Northumbria at Newcastle 23rd October 1995 Abstract There is much confusion and debate about friendship and encapsulation in C++, even after the language has been in use for over ten years. This short paper explores this problem and attempts to provide a solution. Two key concepts are defined, class functions and coupled classes. A way of thinking about these is explored that supports their proper use in C++. It is accepted that C++ can be misused, and this paper tries to help programmers avoid misuse in a small area of the language. Introduction Friendship in C++ is a subject that causes strong opinions to be expressed. C++ has been used for over ten years but the debate still appears to be active. C++ is increasingly popular, so new users are discovering the issues for the first time. And other languages have, or are developing, object oriented features which invite comparison with C++. One school dislikes or just distrusts friendship because it is thought to violate encapsulation. Others insist that friendship is a necessary feature of C++ and is harmless. Stoustrup, the designer of C++, obviously believes that friendship is okay. He quickly dismisses critics with: A friendship declaration was seen as a mechanism similar to that of one protection domain granting a read-write capability to another. It is an explicit and specific part of a class declaration. Consequently, I have never been able to see the recurring assumption that a friend declaration violates encapsulation as 1

anything but a combination of ignorance and confusion with non- C++ terminology. [Stoustrup 94, page 53] Encapsulation and friendship in C++ is a confusing topic. This is particularly so for programmers who are new to object orientation. After some thought, they come to terms with data encapsulation. They accept a paradigm where the only way to access the data parts of an object is through its member functions. They equate the term member function with encapsulation, so anything that is not a member function must violate encapsulation. This confusion is not helped by language specific views of object oriented programming. C++ is not a restrictive language, and it is accepted by most C++ users that its mechanisms, including friendship, can be misused. The freedom to do this is intrinsic. Indeed, one of the design rules used by Stoustrup for the development of C++ is It is more important to allow a useful feature than to prevent every misuse. [Stoustrup 94, page 115] This is a characteristic of C++ that comes from its roots as a systems programming language rather than a training or research language. Here we attempt to provide a consistent approach to friendship and encapsulation in C++ that will reduce the confusion, and hopefully allow us to move on from what has become a rather sterile debate. The approach introduces two general concepts: class functions and coupled classes. These are supported by standard language features: classes, member functions, global friend functions, friend member functions and friend classes. These C++ feature are briefly described here, but more information can be found in [Stroustrup 91], or any other good C++ text book. Friend Functions Consider the following class declaration: class Abc { friend void funct1( Abc& a, int b ); void funct2( int c ); int xdata; 2

The function funct1 is a global friend function of the class Abc. The function funct2 is a public member function of the class. The variable xdata is a private data member. Friend functions are public and by convention (the authors) are put at the top of a class declaration, as shown above. The friends and members of a class have access to its private section. So, the implementations of funct1 and funct2 can access the variable xdata. However, their method of doing this differs. A member function has all the members of its class directly in its scope, so a trivial implementation of funct2 might look like this: void Abc::funct2( int c ) { xdata = c; } A friend has no self so it must explicitly associate an operation with a specific instance of the class, and funct2 might look like this: void funct1( Abc& a, int b ) { a.xdata = b; } Friend and member functions are called in different ways. Given a instance of class Abc named wibble, and an int named wobble. The member functions is called with wibble.funct2(wobble) and the friend function with funct1(wibble,wobble) This has been a fairly neutral description, so we will introduce some of the terminology and usage that seems to cause controversy. A common description of the semantics of a friend function is something like: the declaration of a global function as a friend gives it access to the private section of a class. A simple interpretation of this is usual, which is that a global function can access the private section of a class in C++. This would clearly seem to imply that friendship does indeed violate encapsulation. However, this interpretation is inaccurate. It is caused by a misunderstanding of C++ terminology. Referring to a friend function as global does not mean that it is not part of the class, as the above interpretation assumes. Friend functions are global in the sense that they are not part of the class name space. Consider the following example: 3

class Abc { friend void funct1(); // #1 - same as 2 & 3 void funct1(); void funct2(); class Xyz { friend void funct1(); // #2 - same as 1 & 3 void funct1(); void funct2(); void funct1(); // #3 - same as 1 & 2 void funct2(); Here, here are three different name spaces. They are the classes Abc and Xyz, and the global name space. The name of the friend function funct1 in both classes is in the global name space, so it refers to a single function. The three functions with the name funct2 are distinct. There is no confusion in the following example: Abc abc; Xyz xyz; abc.funct1(); abc.funct2(); xyz.funct1(); xyz.funct2(); funct1(); funct2(); // member of Abc // member of Abc // member of Xyz // member of Xyz // friend of Xyz and Abc // global function The same name can be used for friend functions in different class without referring to the same global function, provided they have different argument lists. It is considered normal practice (by the author at least) for friend functions to access instances of their class as arguments. This gives friend functions with the same name different signatures and function overloading ensures that the correct function is called. 4

The same friend function can be declared in two classes by specifying identical signatures. However, this should only be done with care because it causes the classes to be associated as coupled classes, which are discussed below. There is further confusion caused by the way friend and member functions are associated with the objects they use. The compiler s internal mechanism for binding a friend function to an instance of a class is the same as that used with ordinary functions in the global name space. Friend functions must bind to an object as an argument or unusually as a global variable. This is different from a member function, which is implicitly bound to a particular instance of its class by a this pointer. With a friend, the compiler simply knows that it has been give permission to allow the function access to the private section of its class. The particular instance of the class being accessed must be given explicitly. Class Functions It is less confusing, in C++, to consider friend and member functions as subsets of class functions. Comparing friend and member functions gives a definition of class functions: Friend and member functions are both parts of a class. They are class functions. They are both special sorts of function that have access to the private section of their class. Friend and public member functions form the public interface of their class. Member functions are used with a dot notation: a.funct(b) Friend functions are used like conventional functions: funct(a,b). A member function s name space is restricted to its class. A global friend function s name is in the global name space. The implementation of a member function has unqualified access to the private section of its class. The implementation of a friend function must qualify the names its class members with the name of an instance of its class that is in scope. 5

So the key issue is not encapsulation but interface design. Friend and member functions offer different syntax to the user of a class. Friend functions enable us to provide a richer interface for our classes. Good interface design must be considered. Versatility does not ensure good design. Indeed, it can promote poor design by allowing more choice. So C++ programmers need to know what they are doing, and should not use a language feature just because it is available. They need, like all good programmers, to think beyond the language, considering both the problem being solved and general design issues. Friend Classes Two classes can be associated as friends like this: class Abc { class Xyz { friend class Abc; In this example all the member functions of the class Abc can access the private and public members of the class Xyz. The class Xyz has given permission for this with friend class Abc. The purpose of making classes friends is to allow them to share a common implementation. This is often interpreted as a violation of encapsulation. The view is that a class is the ultimate unit of encapsulation. This may be true for some object oriented languages, but it is not so for C++. Classes can be grouped together as friends to form an encapsulation entity. However, this is not supported by any language feature which externally groups such classes together, such a module, so the encapsulation entity nature of friend classes is not emphasized. 6

Friend classes are need to express some useful idioms in C++. These idioms are not absolutely essential, and some object oriented languages do without them or use a different mechanism. For example, Smalltalk has a meta-class that can provide common implementation for groups of classes. A typical use for a friend class in C++ is as an iterator. An exploration of the need for an iterator and how it might be implemented is a good way to understand the way friend classes are used in C++. A container class, such as a list, sometimes has to be traversed. We might store several items in the list, and then need to calculate some summary of an attribute from each of the items. One way to manage this is to provide the list with a mechanism for accessing each item in turn. We can then step through the list, an item at a time, performing whatever processing is required. This stepping facility would be provided as part of the public interface of a the list class. A simple stepping facility only allows one traversal to be in progress at any one time. In many cases this will be satisfactory, but there are activities that may need two separate traversals through list to be active at the same time. A simple example is inter-item comparisons in the same list. Another more subtle need for concurrent traversals arises from passing a list as a parameter to a function. Consider the situation that while a traversal is in progress, the list is passed to a function as an argument, and that this function also performs a list traversal. If the list class only supports non-concurrent traversals, the function will have to disrupt the calling program s traversal. This side effect is not desirable and can only be avoided by banning or fully supporting concurrent traversals. Concurrent traversals are difficult to prevented. Deep copies can be used, or a flag can be set by the copy constructor, which prevents traversal in all copies. However this does not prevent concurrent traversal when a list is passed by reference. Supporting concurrent traversals using a single class is difficult and can lead to very complicated interfaces. The problems center round maintaining separate pointers to the hidden internal structure of the list without violating encapsulation. We want to support concurrent traversals but we do not want to expose the implementation of the list to the user. We can resolve the problem by encapsulating the pointer to the internal list structure in a class so that it is hidden from the user. However, this will not work unless each instance of the pointer class has access to the hidden implementation of the list. To provide such access in the public interface of the list class really would violate encapsulation. So we reach the conclusion: what is needed is a facility for the definition of a class that can have multiple independent instances created, but which has access to private section of another class. 7

In other words a friend class. For the list example, we can use two classes. A container which manages data storage, and an iterator which manages concurrent traversal. These share a common implementation. They must not be considered as separate encapsulation entities. They are part of the same component. Between them, they provide a user interface for a concurrently traversable list. Coupled Classes Friend classes are just one way in which two classes can be associated as encapsulation entities. To avoid confusion we refer to groups of classes that have this association as coupled classes. It is possible to form coupled classes in three ways: The declaration of a friend function that is a member function of another class. The declaration of a friend class. The declaration of the same friend function in more than one class. The syntax for these methods is as follows: Friend Member Function : class Abc { void funct1( int a ); class Xyz { friend Abc::funct1( int a ); Friend Class : 8

class Abc { class Xyz { friend class Abc; Friend Global Function : class Xyz; // forward reference class Abc { friend funct1( Abc& a, Xyz& b ); class Xyz { friend funct1( Abc& a, Xyz& b ); All these methods are correctly used to achieve the same thing: a multiclass encapsulation entity. In other words, classes that have a shared implementation. If this is not required then this syntax should not be used. A friend class is the general case. Friend member functions are in effect a subset of friend classes. They are used to qualify the amount of sharing 9

that is allowed. This level of detail is not normally needed (by the author at least), but it can act as a check on design intentions, rather like a data type. When friend global functions are used to associate classes, the relationship is not as clearly stated. It is possible to declare a friend function, that causes two classes to share implementation, for reasons other than creating coupled classes. This is poor practice. The encapsulation coupling is a side effect of some other design objective. This is a violation of encapsulation, and it should not be used. Coupled classes, and thus the above mechanisms, should only be used when a multi-class encapsulation entity is required. This only occurs when classes need to share their implementation. Coupled classes do not violate encapsulation anymore than member functions do. They share encapsulation. The concept of coupled classes is not supported by many object oriented design methods. So friends are dismissed as being non-object oriented by users of these methods. This is valid as a local, user imposed, restriction but not as a general idea. (The author does not know of any method supporting coupled classes apart from Booch s latest [Booch 93], which may offer a solution.) Summary This has admittedly been a rather one sided exploration of friends in C++. Its aim is clearly to show that friends are okay in C++. It justifies the use of friendship in C++ as a valid object oriented technique by defining a correct interpretation of its semantics. There are two concepts: class functions and coupled classes. These are supported by four mechanisms: global friend functions, friend member functions, member functions and friend classes. The concepts and mechanisms are related like this: mechanism class function coupled class member function global friend function member friend function friend class Class functions: Friend and member functions are both parts of a class. They are both special sorts of function that have access to the private section of a class. 10

Friend and public member functions form the public interface of the class. Member functions have a class-wide name space, global friend functions do not. The choice between the use of a member or friend function is one of interface syntax. A member functions is used with a dot notation: a.funct(b) It has unqualified access to the private section of the class instance given before the dot. A friend functions is used like a conventional function: funct(a,b) The class instance is passed to the function as an argument. This is accessed using qualified names. Always access the class object as an argument to the function. Think about interface design. Try to keep its syntax simple. Class functions are: Global friend functions Member functions Coupled classes: Are an encapsulation entity. Coupled classes are declared using: Friend classes Friend member functions Common global friend functions They are used only when is is necessary for more than one class to share their implementations. With these simple rules to define a consistent approach, the friendship features of C++ can be used with confidence. They do not violate encapsulation. They support a richer interface to encapsulated data objects than would be possible without them. If these rules are not followed then it is possible that encapsulation will be violated. However, there are much easier ways to cheat in C++ than this [Waldo 94]. Such misuse is an example of bad design practice rather than a language fault. C++ is not a restrictive language. This makes C++ 11

practically useful for a wide range of problems, but it also means that it is not as simple or as elegant to use as some teaching and research languages. C++ is a rich language that can be used in ways that are inappropriately complicated. This is may be clever, but it is bad programming. A clear understanding of some useful ideas and techniques, such as those discussed above, can help to avoid such situations. References [Booch 93] G.Booch, Object Oriented Analysis and Design with Applications, 2nd Edition. Benjamin Cummings, 1993. [Stroustrup 91] B.Stoustrup, The C++ Programming Language, 2nd Edition. Addison Wesley, 1991. [Stoustrup 94] B.Stoustrup, The Design and Evolution of C++. Addison Wesley, 1994. [Waldo 94] J.Waldo, Secrets and Privacy in C++. Unix Review, pp.63-66, October 1994. 12