Part X. Advanced C ++

Similar documents
Part III. Advanced C ++

CS93SI Handout 04 Spring 2006 Apr Review Answers

Part II. C continued. Philip Blakely (LSC) Advanced C++ 35 / 119

Part XI. Algorithms and Templates. Philip Blakely (LSC) C++ Introduction 314 / 370

Part VII. Object-Oriented Programming. Philip Blakely (LSC) C++ Introduction 194 / 370

Item 4: Extensible Templates: Via Inheritance or Traits?

Advanced C ++ Philip Blakely. Laboratory for Scientific Computing, University of Cambridge. Philip Blakely (LSC) Advanced C++ 1 / 119

Templates Templates are functions or classes that are parameterized. We have already seen a few in STL:

Lecture 2 Polymorphism, Traits, Policies, and Inheritance

Part VIII. More classes. Philip Blakely (LSC) C++ Introduction 226 / 370

CSCI 102L - Data Structures Midterm Exam #1 Fall 2011

COMP6771 Advanced C++ Programming

A brief introduction to C++

Fast Introduction to Object Oriented Programming and C++

Software Design Tidbits. Bálint Joó, Scientific Computing Group Jefferson Lab

I m sure you have been annoyed at least once by having to type out types like this:

Midterm Review. PIC 10B Spring 2018

An Introduction to Template Metaprogramming

CS

Programming in C++ Prof. Partha Pratim Das Department of Computer Science and Engineering Indian Institute of Technology, Kharagpur

Lecture Topics. Administrivia

Advanced C++ 4/13/2017. The user. Types of users. Const correctness. Const declaration. This pointer and const.

Overload Resolution. Ansel Sermersheim & Barbara Geller ACCU / C++ June 2018

Intermediate C++ 1/83

The issues. Programming in C++ Common storage modes. Static storage in C++ Session 8 Memory Management

QUIZ on Ch.5. Why is it sometimes not a good idea to place the private part of the interface in a header file?

05-01 Discussion Notes

Why use inheritance? The most important slide of the lecture. Programming in C++ Reasons for Inheritance (revision) Inheritance in C++

C++ (Non for C Programmer) (BT307) 40 Hours

Cpt S 122 Data Structures. Course Review Midterm Exam # 2

Tokens, Expressions and Control Structures

AIMS Embedded Systems Programming MT 2017

COMP6771 Advanced C++ Programming

std::optional from Scratch

CSCI-1200 Data Structures Spring 2018 Lecture 14 Associative Containers (Maps), Part 1 (and Problem Solving Too)

Software Engineering Concepts: Invariants Silently Written & Called Functions Simple Class Example

What will happen if we try to compile, link and run this program? Do you have any comments to the code?

Outline. 1 Function calls and parameter passing. 2 Pointers, arrays, and references. 5 Declarations, scope, and lifetimes 6 I/O

Part V. Memory and pointers. Philip Blakely (LSC) C++ Introduction 145 / 370

A Crash Course in (Some of) Modern C++

Ch. 12: Operator Overloading

Objects Managing a Resource

ECE 2400 Computer Systems Programming Fall 2017 Topic 15: C++ Templates

Object-Oriented Programming for Scientific Computing

TDDD38 - Advanced programming in C++

04-17 Discussion Notes

When we program, we have to deal with errors. Our most basic aim is correctness, but we must

Problem Solving with C++

C++ for numerical computing - part 2

QUIZ. What is wrong with this code that uses default arguments?

And Even More and More C++ Fundamentals of Computer Science

CMSC 341 Lecture 6 Templates, Stacks & Queues. Based on slides by Shawn Lupoli & Katherine Gibson at UMBC

Laboratorio di Tecnologie dell'informazione. Ing. Marco Bertini

Lecture 2, September 4

Overload Resolution. Ansel Sermersheim & Barbara Geller Amsterdam C++ Group March 2019

C++ Coding Standards. 101 Rules, Guidelines, and Best Practices. Herb Sutter Andrei Alexandrescu. Boston. 'Y.'YAddison-Wesley

Week 8: Operator overloading

Static If I Had a Hammer

04-24/26 Discussion Notes

Advanced Systems Programming

CSE 100: C++ TEMPLATES AND ITERATORS

COMP322 - Introduction to C++ Lecture 10 - Overloading Operators and Exceptions

Instantiation of Template class

CSc 328, Spring 2004 Final Examination May 12, 2004

EL2310 Scientific Programming

Assignment 1: grid. Due November 20, 11:59 PM Introduction

Appendix. Grammar. A.1 Introduction. A.2 Keywords. There is no worse danger for a teacher than to teach words instead of things.

C++ Templates. David Camp

Module 10 Inheritance, Virtual Functions, and Polymorphism

A brief introduction to C programming for Java programmers

CSE 374 Programming Concepts & Tools. Hal Perkins Fall 2015 Lecture 19 Introduction to C++

CSC 210, Exam Two Section February 1999

Exception Namespaces C Interoperability Templates. More C++ David Chisnall. March 17, 2011

CS102 C++ Exception Handling & Namespaces

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

G52CPP C++ Programming Lecture 16

CSE 374 Programming Concepts & Tools. Hal Perkins Spring 2010

Rvalue References, Move Semantics, Universal References

Introduction to Linked Lists. Introduction to Recursion Search Algorithms CS 311 Data Structures and Algorithms

CSE 333. Lecture 11 - constructor insanity. Hal Perkins Paul G. Allen School of Computer Science & Engineering University of Washington

Vector and Free Store (Vectors and Arrays)

Vector and Free Store (Pointers and Memory Allocation)

Topics. bool and string types input/output library functions comments memory allocation templates classes

Templates (again) Professor Hugh C. Lauer CS-2303, System Programming Concepts

Lecture Notes on Memory Layout

Inheritance. OOP components. Another Example. Is a Vs Has a. Virtual Destructor rule. Virtual Functions 4/13/2017

CS11 Introduction to C++ Fall Lecture 6

Reminder: compiling & linking

2 ADT Programming User-defined abstract data types

Modern and Lucid C++ Advanced for Professional Programmers. Part 7 Compile-Time Computation. Department I - C Plus Plus

Resource Management With a Unique Pointer. Resource Management. Implementing a Unique Pointer Class. Copying and Moving Unique Pointers

CSCI-1200 Data Structures Spring 2016 Lecture 6 Pointers & Dynamic Memory

CE221 Programming in C++ Part 1 Introduction

Programming, numerics and optimization

The C++ Programming Language, Core Working Group. Title: Unary Folds and Empty Parameter Packs (revision 1)

Outline. Java Models for variables Types and type checking, type safety Interpretation vs. compilation. Reasoning about code. CSCI 2600 Spring

An Introduction to C++

Client Code - the code that uses the classes under discussion. Coupling - code in one module depends on code in another module

use static size for this buffer

QUIZ Friends class Y;

Transcription:

Part X Advanced C ++ topics Philip Blakely (LSC) Advanced C++ 158 / 217

References The following are highly regarded books. They are fairly in-depth, and I haven t read them in their entirity. However, if you want to write robust code that will not surprise you or others using your code, and which can be extended in the future, you should at least have a look at them. Effective C ++, Scott Meyers Exceptional C ++, Herb Sutter Modern C ++ Design, Andrei Alexandrescu Philip Blakely (LSC) Advanced C++ 159 / 217

Base class construction Suppose you have a base-class containing some data, with a relatively complex initialization. Any derived class should not copy-paste the base s constructor. When constructing any object, the base-object is always constructed first, and this can be called from the derived class: class SpecialVector : Vector{ SpecialVector(int SIZE, int d) : Vector(SIZE), m data(d){ int m data; ; Note that the Vector initializer comes first. You could put it later, but it will always be initialized first. gcc warns (with -Wall) if the constructor order is different from what will actually happen. See Examples/baseclass.C Philip Blakely (LSC) Advanced C++ 160 / 217

Resource Acquisition Is Initialization A good programming practice to abide by is RAII. The idea is that all finite resources such as allocated memory, file handles, etc. are handled through object instances. Any class that handles any resource must ensure that once the object ceases to exist, the resource it handles is freed (or is passed onto another object) For example, a Vector object which allocates memory should have: Vector::Vector(size t s){ m data = new double[s]; Vector:: Vector(){ delete[] m data;... as well as similar functionality from other constructors. Philip Blakely (LSC) Advanced C++ 161 / 217

RAII ctd However, for more complex objects, more care must be taken: DoubleVector::DoubleVector(size t s){ m data = new double[s]; m data2 = new double[s 10]; DoubleVector:: DoubleVector(){ delete[] m data; delete[] m data2; What happens if the first allocation succeeds and the second fails? If you do not catch exceptions, then the std::bad alloc will propagate all the way up the call-stack and cause the program to terminate. If you do handle exceptions, then the object will potentially be left in an uninitialized state, and the first block of data will be leaked. You should put a try / catch block around the second new statement, and delete m data[] before re-throwing the exception. Philip Blakely (LSC) Advanced C++ 162 / 217

Base class destruction We saw previously that the base class is constructed before the derived class. Conversely, on destruction, the derived class is destroyed first, before the base class. If the derived class constructor throws an exception, then any base classes already constructed are destroyed (in order). See Examples/raii.C for an example. Philip Blakely (LSC) Advanced C++ 163 / 217

Polymorphism and RAII Suppose you have a simple polymorphic class with dynamically allocated data: class Base{ public: Base(){ m data = new int[10]; Base(){ delete[] m data; private: int m data; ; class Derived : public Base{ public: Derived(){ m mydata = new int[10]; Derived(){ delete[] m mydata; private: int m mydata; ; Philip Blakely (LSC) Advanced C++ 164 / 217

Polymorphism and RAII You would naturally use: Base b = new Derived; delete b; However, this leaks the memory allocated by the Derived object; its destructor is never called. Only the Base destructor is called. The solution is to make the Base destructor virtual: virtual Base(){ delete[] m data; Now delete b causes first the Derived destructor then the Base destructor to be called. See Examples/destructors.C for the full example. Philip Blakely (LSC) Advanced C++ 165 / 217

Polymorphism and RAII Note that any memory allocated by member data of the Derived object is also leaked. For example, even if Derived only contains a std::vector<int>, and Base contains no internal data, the Base class must still have a virtual (and empty) destructor. Destructors of member data are only called when the class s destructor (auto-generated or explicitly written) is called. Destructors are not virtual by default because making them so results in extra code. The principle is don t pay for what you don t need. However, a non-virtual destructor on a polymorphic base-class is almost always wrong. Philip Blakely (LSC) Advanced C++ 166 / 217

SFINAE You may see references to Substitution Failure Is Not An Error This refers to a C ++ feature that allows us to selectively allow compilation of templated functions. It prevents instantiation of templated functions that result in ill-defined types. An example is the use of std::enable if<bool b, typename T> If the parameter b is true, then: std::enable if<true, T>::type is the same as T. If b is false, then std::enable if<false, T>::type does not exist. Philip Blakely (LSC) Advanced C++ 167 / 217

SFINAE example From Examples/sfinae.C: template<typename T> void print(t t, typename std::enable if<std::is pointer<t>::value, char>::type = 0){ std::cout << "Pointer to " << t << std::endl; template<typename T> void print(t t, typename std::enable if<!std::is pointer<t>::value, char>::type = 0){ std::cout << "Value = " << t << std::endl; int a = 9; print(a); print(&a); Philip Blakely (LSC) Advanced C++ 168 / 217

SFINAE example The first parameter is each print is the plain T so that the compiler can deduce the type. The second parameter is either non-existent or an char, whose value defaults to zero. This looks like partial-template specialization, but isn t. What it really is are two overloaded functions called print, only one of which will ever be considered for any particular type T. because the second parameter does not give a valid type in one case. The non-working version does not give an error because Substitution Failure Is Not An Error. Philip Blakely (LSC) Advanced C++ 169 / 217

SFINAE example Similarly, you can make the functions differ only on their return type: template<typename T> typename std::enable if<std::is pointer<t>::value>::type print2(t t){ std::cout << "Pointer to " << t << std::endl; template<typename T> typename std::enable if<!std::is pointer<t>::value>::type print2(t t){ std::cout << "Value = " << t << std::endl; Specifying no type for enable if defaults to void. The use of enable if often requires the use of a dummy function parameter or template parameter. Philip Blakely (LSC) Advanced C++ 170 / 217

Curriously Recurring Template Pattern You may sometimes need to implement a base class as an interface for a templated class, but whose functionality depends on the templated class. A classic example is cloning. You want the functionality: Vehicle mycar = new Car; Vehicle anothercar = mycar >clone(); This cannot be implemented in Vehicle normally as Vehicle does not have access to all elements of Car, and so would slice off any data not contained in Vehicle. We could implement a clone function for every new type derived from Vehicle. That seems error-prone and wasteful. Philip Blakely (LSC) Advanced C++ 171 / 217

Curiously Recurring Template Pattern Instead, insert a templated class (see Examples/crtp.C): class Vehicle{ public: virtual Vehicle clone()const = 0; ; template<typename Derived> class VehicleInterface : public Vehicle{ virtual Vehicle clone()const{ return new Derived( static cast<const Derived >(this)); ; class Car : public VehicleInterface<Car>{; Now, the clone() function works because the fully-derived type is known and used at instantiation. This is Curiously Recurring because it seems to occur a lot, not because it is recursive. This approach can be used to reduce copy-pasting for other class member functions as well. Philip Blakely (LSC) Advanced C++ 172 / 217

Has-a or is-a? In the first lecture series, you implemented a class hierarchy structure. One important approach to designing a class structure is to consider whether a kind of object is a particular kind of more general object, or whether the more general object should contain this new object type. This is abbreviated to a is-a or has-a relationship. For example, a Car is a kind of Vehicle: struct Car : public Vehicle{ ; but it contains an Engine (has-a) and other components: struct Car : public Vehicle{ Engine m engine; ; Philip Blakely (LSC) Advanced C++ 173 / 217

Other considerations Another relationship between classes is the implemented-in-terms-of approach. For example, a (horribly inefficient) Vector class could be implemented in terms of a List class. The Vector should not be convertible to a List, and it will probably not use any of the internal implementation details of List. Thus we should have: class Vector{ public: Vector(size t s); double operator[](size t i)const; private: List m list; ; Philip Blakely (LSC) Advanced C++ 174 / 217

More object-orientation If you implement a polymorphic set of classes, ensure that whatever constraints the generic interface (base-class) suggests or enforces are also enforced by all derived classes. Recall the example from the exercises in the first C ++ course: a Circle is not a kind-of Ellipse. More details of object orientation can be found in Sutter s Exceptional C ++ : Item 24. Philip Blakely (LSC) Advanced C++ 175 / 217

Factory construct - why In modular programs, or at least ones where multiple options are open to the user, you often need a function like: const Shape getshape(const std::string& name){ if(name == "Sphere") return new Sphere; if(name == "Triangle") return new Triangle; return nullptr; Maintaining this kind of function is error-prone, and needs updating every time you add a new object. A better (more complicated to set up, but easier to maintain) approach is the Factory construct. Philip Blakely (LSC) Advanced C++ 176 / 217

Factory construct Consider a mapping: std::map<std::string, const Shape ( )()> factory; We can now use: const Shape getshape(const std::string& name){ if(factory.find(name)!= factory.end()){ return factory[name](); return nullptr; Note that the stored object is a pointer to function that, when passed no parameters, returns a pointer to a constant Shape. Philip Blakely (LSC) Advanced C++ 177 / 217

Factory construct - initialize Now, each Shape-derived type needs to take the form: class Sphere : public Shape{ public: static const Shape create(); private: static const bool isinfactory; ; const Shape Sphere::create(){ return new Sphere; const bool Sphere::isInFactory = factory.insert( std::make pair("sphere", Sphere::create) ).second; Sphere dummy sphere;... recalling that the insert function returns an iterator and a boolean indicating whether insertion succeeded. Philip Blakely (LSC) Advanced C++ 178 / 217

Factory construct - how? The reason that this approach works is that the static const bool members of the Sphere and Triangle have to be initialized before your code starts. They are therefore initialized, before main is called, using the insert function call. There is no guarantee about the order in which the various function calls occur. If the dummy sphere variable were not specified, the compiler would not necessarily generate code to initialize the member data of an unused class. See Examples/factory.C for the full code. Philip Blakely (LSC) Advanced C++ 179 / 217

Templated function calling Outline 24 Templated function calling Philip Blakely (LSC) Advanced C++ 180 / 217

Templated function calling Templated functions Functions can be templated: template<typename T> T sum(const std::vector<t>& v); They cannot be partially specialized: template<typename T, typename S> std::vector<t> product(const std::vector<t>&, const std::vector<s>&){... template<typename T> std::vector<t> product(const std::vector<t>&, const std::vector<t>&){... // Invalid This is because otherwise it becomes difficult to separate overloaded function calls from partial template specialisations. See http://ww.gotw.ca/publications/mill17.htm for a discussion of the problems. Philip Blakely (LSC) Advanced C++ 181 / 217

Templated function calling Ambiguous templated functions You may have tried to compile: double dt = calctimestep(); double dtactual = std::min(dt, 1); and been surprised when it failed. Even though 1 is an integer, surely the compiler can figure out that you want std::min<double>? No (not without a lot of type traits to indicate which types can be promoted). There are two solutions to the above: double dtactual = std::min(dt, 1.0); double dtactual = std::min<double>(dt, 1); The first is probably nicer. Philip Blakely (LSC) Advanced C++ 182 / 217

Templated function calling Ambiguous templated functions The reason this fails is that std::min<t>(const T&, const T&) only has one template parameter, and a consistent T cannot be deduced. The C ++ standard specifies this form, rather than a more general one. Philip Blakely (LSC) Advanced C++ 183 / 217

Templated function calling Scope of templated classes If you have a templated class with a templated base, you may encounter confusion: template<typename T> struct A{ int mysize(){ return sizeof(t); ; template<typename T> struct B : A<T>{ void print(){ std::cout << mysize() << std::endl; ; will fail to compile: error: there are no arguments to mysize that depend on a template parameter, so a declaration of mysize must be available... which is not very revealing. Philip Blakely (LSC) Advanced C++ 184 / 217

Templated function calling Scope of templated classes The reason is that C ++ has a two-phase name look-up. The first time that the compiler parses a class or function it must be able to work out what the types of any non-template-parameter-dependent functions or types are. In the previous slide, mysize() when called does not depend on a template parameter, and therefore the compiler looks through the set of functions, variables, and types that are available, without knowing T. Since mysize() will actually be found in the base-class only when T is known, this phase fails. The solution is to make the call clearly depend on T: std::cout << this >mysize() << std::endl; std::cout << A<T>::mySize() << std::endl; Either of these causes name-look-up for mysize() to be delayed until the second phase, when T is known. Philip Blakely (LSC) Advanced C++ 185 / 217

Templated function calling Scope of templated classes Further problems arise if you want to call a templated member function of a templated base. If the template parameter can be deduced, then it s simple: template<typename T> template<typename S> int A::itsSize(S a){ return sizeof(s); template<typename T> void B<T>::print(){ std::cout << this >itssize(2) << std::endl; (See Examples/templatedBase.C) Philip Blakely (LSC) Advanced C++ 186 / 217

Templated function calling Calling a templated member If you want a different (or non-deducible) template parameter, you must use: std::cout << this >template itssize<double>(2) << std::endl; Otherwise, this->itssize<double> is interpreted as an unresolved overloaded function, followed by a less-than sign, followed by double This will not succeed (possibly unless you ve overloaded a very weird operator<, and even then I think it might be impossible). Philip Blakely (LSC) Advanced C++ 187 / 217

Templated function calling typename Due to the two-phase look-up, the compiler sometimes needs to be told in advance whether a typename or something else will result. template<typename X> struct Y : X{ using typename X::F; void f(){ int x = F(); ; The F() construct on its own could either be a function call, or a construction of an object of type F using no parameters. Further, F is only brought into scope by the using directive. The typename tells the compiler to expect F to be a type. If typename was absent then X::F would be assumed to be a function. Whichever is required will be checked against the first phase s deductions when X is known, on the second phase. See Examples/typename.C for a complete demonstration. Philip Blakely (LSC) Advanced C++ 188 / 217