Constructors.

Slides:



Advertisements
Similar presentations
Throwing and catching exceptions
Advertisements

Recursive binary search
For loops.
Templates.
Introduction to classes
Static variables.
Default values of parameters
Pointers.
Binary search.
Command-line arguments
Throwing exceptions.
Console input.
Dangling pointers.
This.
Floating-point primitive data types
Dynamically allocating arrays within structures
Dynamic memory allocation
Break statements.
Introduction to classes
Linked Lists.
Wild pointers.
Console output.
Logical operators.
The comma as a separator and as an operator
Bucket sort.
The call stack and recursion and parameters revisited
The ternary conditional operator
Dynamically allocating structures
Memory leaks.
Bit-wise and bit-shift operators
Sorting algorithms.
Command-line arguments
Passing pointers as parameters to and from functions
Templated Linked Lists
Polymorphism.
Dynamically allocating arrays
Insertion sort.
Problems with pointers
A list-size member variable
Protecting pointers.
Code-development strategies
Throwing exceptions.
Anatomy of a program.
Console output.
Insertion sort.
Pointers as arguments and return values
Reference variables, pass-by-reference and return-by-reference
Addresses and pointers
Default values of parameters
Class variables and class functions
Operator overloading.
The std::string class.
Templates.
This.
Dynamic memory allocation
Insertion sort.
Sorting algorithms.
Issues with classes.
Dangling pointers.
Dynamic allocation of classes
Encapsulation.
Destructors.
Counting sort.
Searching and sorting arrays
Protecting pointers.
Data structures: class
An array class: constructor and destructor
This.
Recursive binary search
Recursive functions.
Presentation transcript:

Constructors

Outline In this lesson, we will: Describe issues with the primitive data types Introduce the 3-body problem and an attempt to solve it Introduce the struct keyword and member variables Create a 3-dimensional vector data structure Describe assigning to and using the member variables Describe passing instances of data structures as arguments We will create a library of functions for vectors Initialize instances of data structures Revisit our 3-body problem Determine if we have a solution to arrays

Constructors We will look at constructors by describing two classes: A rational number class A date-time group class

Limitations of primitive data types Suppose you want to create a rational-number class class Rational { public: int numerator; int denominator; };

Limitations of primitive data types Let’s author a few functions: std::string Rational::to_string() const { if ( denominator == 1 ) { return std::to_string( numerator ); } else { return std::to_string( numerator ) << "/" << std::to_string( denominator ); } Rational Rational::operator+( Rational const &q ) const { return Rational{ numerator*q.denominator + denominator*q.numerator, denominator*q.denominator };

Limitations of primitive data types Here are two more: Rational Rational::operator+( Rational const &q ) const { return Rational{ numerator*q.denominator + denominator*q.numerator, denominator*q.denominator }; } Rational Rational::operator*( Rational const &q ) const { return Rational{ numerator*q.numerator,

Limitations of primitive data types Suppose you tried the following: Rational p{1, 2}; Rational q{p + 5}; This should equal to the rational number 11/2, but we can only do this right now with Rational q{p + Rational{5, 1}}; For this, we have to tell the compiler how we can add an int to or multiply an int by a rational number

Limitations of primitive data types We could implement additional functions such as - and /, but these are left to you Rational Rational::operator+( int n ) const { return Rational{ numerator + denominator*n, denominator }; } Rational Rational::operator*( int n ) const { return Rational{ numerator*n,

Limitations of primitive data types What happens if we do the following? Rational Rational::operator==( Rational const &q ) const { return ( numerator == q.numerator ) && (denominator == q.denominator); } Rational Rational::operator==( int n ) const { return (numerator == n) && (denominator == 1);

Limitations of primitive data types Of course, this is wrong, because all of the following are equal: We have two choices: Perform the following calculation: Always save rational numbers in the same format: Lowest terms (no common factors) The denominator must be positive

Limitations of primitive data types Of course, this is wrong, because all of the following are equal: We have two choices: Perform the following calculation: Always save rational numbers in the same format: Lowest terms (no common factors) The denominator must be positive All the above numbers would be stored as

Limitations of primitive data types The problem: We cannot force the programmer not to use: Rational q{-1, 2}; Suppose, instead, we write a function: void Rational::normalize() { int n{ gcd( numerator, denominator ) }; numerator /= n; denominator /= n; if ( denominator < 0 ) { numerator = -numerator; denominator = -denominator; }

Limitations of primitive data types We would thus require the user to do: Rational q{17, -38}; std::cout << q.to_string() << std::endl; q.normalize(); What if the user forgets? Output: 17/-83 -1/2

Limitations of primitive data types Can we force the user to use this function? Additionally, we need to use this internally: Rational p{-1, 2}; Rational q{-2, 1}; Rational r{p*q}; std::cout << r << std::end; Thus, we must also include: Rational Rational::operator+( Rational const &q ) const { Rational result{ numerator*q.denominator + denominator*q.numerator, denominator*q.denominator }; result.normalize(); return result; } Output: 2/2 The output should be 1

Limitations of primitive data types Instead, object-oriented program also allows the author of a class to specify a function that must be called: That function is called a constructor and has the same name as the class itself It has no return value—it simply ensures initialization occurs when a new rational number is declared: Let us rewrite our class: Rational::Rational( int first, int second ): numerator{ first }, denominator{ second } { normalize(); }

Limitations of primitive data types Let’s look at this: Rational::Rational( int first, int second ): numerator{ first }, denominator{ second } { normalize(); } Name of the class Parameters to the constructor Default values for the member variables Operations to be performed on the initialized instance

Limitations of primitive data types Now the user can do the following: Rational p(1, -2); Rational q(-21, 42); Rational r(124, -248); std::cout << p << std::end; std::cout << q << std::end; std::cout << r << std::end; Output: -1/2

Limitations of primitive data types Let’s look at a second class: a date-time group class DTG { public: int year; // Each of these is one byte from -128 to 127 signed char month; // One byte signed char day; // One byte signed char hour; // One byte signed char minute; // One byte signed char second; // One byte };

Date-time group Let’s look at a second class: a date-time group class DTG { public: int year; // Each of these is one byte from -128 to 127 signed char month; // 1 - 12 signed char day; // 1 - 31 signed char hour; // 0-23 signed char minute; // 0-59 signed char second; // 0-59 };

dtg printing We could print out the date: Output: 1969-7-16T9:32:0 std::string DTG::to_string_iso() const { return std::to_string( year ) + "-" + std::to_string( month ) + "-" + std::to_string( day ) + "T" + std::to_string( hour ) + ":" + std::to_string( minute ) + ":" + std::to_string( second ) + "J"; // 'J' for 'local time' } Output: 1969-7-16T9:32:0 1969-11-14T11:22:0 1970-4-11T14:13:0

dtg printing We could print out the date: Output: 1969-7-16T9:32:0 int main() { DTG apollo11{ 1969, 07, 16, 09, 32, 00 }; DTG apollo12{ 1969, 11, 14, 11, 22, 00 }; DTG apollo13{ 1970, 04, 11, 14, 13, 00 }; DTG apollo27{ -32, 0, 52, 67, -23, 5532523532315 }; std::cout << apollo11 << std::endl; std::cout << apollo12 << std::endl; std::cout << apollo13 << std::endl; return 0; } Output: 1969-7-16T9:32:0 1969-11-14T11:22:0 1970-4-11T14:13:0 -32-0-52T67:-23:27

dtg constructor Our constructor could ensure sanity: DTG::DTG( int y, signed char m, signed char d, signed char hr, signed char min, signed char sec ): year{y}, month{m}, day{d}, hour{hr}, minute{min}, second{sec} { validate_year( year ); // Not 0: 1 CE comes after 1 BCE validate_month( month ); // Between 1 and 12 validate_day( year, month, day ); // Between 1 and 28, 29, 30 or 31 depending on year validate_hour( hour ); // Between 0 and 23 validate_minute( minute ); // Between 0 and 59 validate_second( second ); // Between 0 and 59 }

Summary Following this lesson, you now Understand the struct keyword Know how to declare, access and manipulate the member variables Know how to pass instances of data structures to functions Understand how to initialize instances Understand how data structures can contain data structures Know that this simple solution cannot solve our issues with arrays

References [1] No references?

Colophon These slides were prepared using the Georgia typeface. Mathematical equations use Times New Roman, and source code is presented using Consolas. The photographs of lilacs in bloom appearing on the title slide and accenting the top of each other slide were taken at the Royal Botanical Gardens on May 27, 2018 by Douglas Wilhelm Harder. Please see https://www.rbg.ca/ for more information.

Disclaimer These slides are provided for the ece 150 Fundamentals of Programming course taught at the University of Waterloo. The material in it reflects the authors’ best judgment in light of the information available to them at the time of preparation. Any reliance on these course slides by any party for any other purpose are the responsibility of such parties. The authors accept no responsibility for damages, if any, suffered by any party as a result of decisions made or actions based on these course slides for any other purpose than that for which it was intended.