Chapter 8(10) - Part 1

Lakukan tugas rumah & ujian kamu dengan baik sekarang menggunakan Quizwiz!

struct vs class

In C++, classes and structs are essentially the same. Note that the only significant difference is the public: keyword in the class. struct DateStruct { int year; int month; int day; }; class DateClass { public: int m_year; int m_month; int m_day; };

How should you name your classes

Name your classes starting with a capital letter.

What prefix is useful to put in front of member variable's identifier

Using the "m_" prefix for member variables helps distinguish member variables from function parameters or local variables inside member functions. This is useful for several reasons. First, when we see an assignment to a variable with the "m_" prefix, we know that we are changing the state of the class instance. Second, unlike function parameters or local variables, which are declared within the function, member variables are declared in the class definition. Consequently, if we want to know how a variable with the "m_" prefix is declared, we know that we should look in the class definition instead of within the function.

initializing const array in class

class Something { private: const int m_array[5]; public: Something(): m_array { 1, 2, 3, 4, 5 } // use uniform initialization to initialize our member array { } };

classes (and structs) can also contain functions! Functions defined inside of a class are called

member functions (or sometimes methods). class DateClass { public: int m_year; int m_month; int m_day; void print() // defines a member function named print() { std::cout << m_year << "/" << m_month << "/" << m_day; } }; Member functions can be defined inside or outside of the class definition. We'll define them inside the class for now (for simplicity)

Access functions typically come in two flavors:

getters and setters.

If all of the initializers don't fit on a single line (or the initializers are non-trivial) in a class

then you can space them out, one per line: class Something { private: int m_value1; double m_value2; char m_value3; float m_value4; public: Something(int value1, double value2, char value3='c', float value4=34.6f) : m_value1{ value1 }, m_value2{ value2 }, m_value3{ value3 }, m_value4{ value4 } { } };

If your class has any other constructors, the implicitly generated constructor will

will not be provided. For example: class Date { private: int m_year{ 1900 }; int m_month{ 1 }; int m_day{ 1 }; public: Date(int year, int month, int day) // normal non-default constructor { m_year = year; m_month = month; m_day = day; } // No implicit constructor provided because we already defined our own constructor }; int main() { Date date{}; // error: Can't instantiate object because default constructor doesn't exist and the compiler won't generate one Date today{ 2020, 1, 19 }; // today is initialized to Jan 19th, 2020 return 0; }

Advantage of object-oriented programming

you.driveTo(work); instead of driveTo(you, work); This not only reads more clearly, it also makes it clearer who the subject is (you) and what behavior is being invoked (driving somewhere). Rather than being focused on writing functions, we're focused on defining objects that have a well-defined set of behaviors. This is why the paradigm is called "object-oriented".

Classes containing classes

#include <iostream> class A { public: A() { std::cout << "A\n"; } }; class B { private: A m_a; // B contains A as a member variable public: B() { std::cout << "B\n"; } }; int main() { B b; return 0; } OUTPUT: A B When variable b is constructed, the B() constructor is called. Before the body of the constructor executes, m_a is initialized, calling the class A default constructor. This prints "A". Then control returns back to the B constructor, and the body of the B constructor executes. This makes sense when you think about it, as the B() constructor may want to use variable m_a -- so m_a had better be initialized first!

Initializing classes inside classes

#include <iostream> class A { public: A(int x) { std::cout << "A " << x << '\n'; } }; class B { private: A m_a; public: B(int y) : m_a{ y-1 } // call A(int) constructor to initialize member m_a { std::cout << "B " << y << '\n'; } }; int main() { B b{ 5 }; return 0; }

Member functions vs non-member functions (Passing data)

1. The key point is that with non-member functions, we have to pass data to the function to work with. With member functions, we can assume we always have an implicit object of the class to work with!

Mixing access specifiers

A class can (and almost always does) use multiple access specifiers to set the access levels of each of its members. There is no limit to the number of access specifiers you can use in a class. In general, member variables are usually made private, and member functions are usually made public.

determine who has access to the members that follow the specifier.

Access specifiers class DateClass { public: // note use of public keyword here, and the colon int m_month; // public, can be accessed by anyone int m_day; // public, can be accessed by anyone int m_year; // public, can be accessed by anyone }; int main() { DateClass date; date.m_month = 10; // okay because m_month is public date.m_day = 14; // okay because m_day is public date.m_year = 2020; // okay because m_year is public return 0; } Each of the members "acquires" the access level of the previous access specifier (or, if none is provided, the default access specifier).

short public function whose job is to retrieve or change the value of a private member variable.

An access function

is a special kind of class member function that is automatically called when an object of that class is instantiated.

Constructors are typically used to initialize member variables of the class to appropriate default or user-provided values, or to do any setup steps necessary for the class to be used class Fraction { private: int m_numerator; int m_denominator; public: Fraction() // default constructor { m_numerator = 0; m_denominator = 1; } };

Purposes of constructor

Constructors actually serve two purposes. First, constructors determine who is allowed to create an object. That is, an object of a class can only be created if a matching constructor can be found. Second, constructors can be used to initialize objects. Whether the constructor actually does an initialization is up to the programmer. It's syntactically valid to have a constructor that does no initialization at all (the constructor still serves the purpose of allowing the object to be created, as per the above).

Constructors vs regular functions

Constructors must have the same name as the class (with the same capitalization) Constructors have no return type (not even void)

Telling the compiler to create a default constructor, even if there are other user-provided constructors.

Date() = default; //these are constructors in class Date(int year, int month, int day) // normal non-default constructor { m_year = year; m_month = month; m_day = day; } Date date{}; // date is initialized to Jan 1st, 1900 Date today{ 2020, 10, 14 }; // today is initialized to Oct 14th, 2020 Using = default is almost the same as adding a default constructor with an empty body. The only difference is that = default allows us the safely initialize member variables even if they don't have an initializer:

Recommendation for initializing variables in class

For best results, the following recommendations should be observed: 1) Don't initialize member variables in such a way that they are dependent upon other member variables being initialized first (in other words, ensure your member variables will properly initialize even if the initialization ordering is different). 2) Initialize variables in the initializer list in the same order in which they are declared in your class. This isn't strictly required so long as the prior recommendation has been followed, but your compiler may give you a warning if you don't do so and you have all warnings turned on.

struct vs class (storing data and functions)

For example, it's fair to assume a class will clean up after itself (e.g. a class that allocates memory will deallocate it before being destroyed), but it's not safe to assume a struct will. Consequently, we recommend using the struct keyword for data-only structures, and the class keyword for defining objects that require both data and functions to be bundled together.

How to use second constructor with parameters(initialize a class object with values we want)

Fraction(int numerator, int denominator=1) //constructor inside a class { assert(denominator != 0); m_numerator = numerator; m_denominator = denominator; } Fraction fiveThirds{ 5, 3 }; // Brace initialization, calls Fraction(int, int) Fraction threeQuarters(3, 4); // Direct initialization, also calls Fraction(int, int) Fraction six{ 6 }; // calls Fraction(int, int) constructor, second parameter uses default value Since C++11, we prefer brace initialization. There is another special constructor that might make brace initialization do something different, in that case we have to use direct initialization.

are functions that return the value of a private member variable.

Getters (also sometimes called accessors) Getters should provide "read-only" access to data. Therefore, the best practice is that they should return by value or const reference (not by non-const reference). A getter that returns a non-const reference would allow the caller to modify the actual object being referenced, which violates the read-only nature of the getter (and violates encapsulation).

are members of a class that can only be accessed by other members of the class.

Private members class DateClass // members are private by default { int m_month; // private by default, can only be accessed by other members int m_day; // private by default, can only be accessed by other members int m_year; // private by default, can only be accessed by other members }; int main() { DateClass date; date.m_month = 10; // error date.m_day = 14; // error date.m_year = 2020; // error return 0; }

are members of a struct or class that can be accessed from outside of the struct or class

Public members struct DateStruct // members are public by default { int month; // public by default, can be accessed by anyone int day; // public by default, can be accessed by anyone int year; // public by default, can be accessed by anyone }; int main() { DateStruct date; date.month = 10; date.day = 14; date.year= 2020; return 0; }

Declaring second constructor with parameters

These two constructors can coexist peacefully in the same class due to function overloading. class Fraction { private: int m_numerator; int m_denominator; public: Fraction() // default constructor { m_numerator = 0; m_denominator = 1; } // Constructor with two parameters, one parameter having a default value Fraction(int numerator, int denominator=1) { assert(denominator != 0); m_numerator = numerator; m_denominator = denominator; } int getNumerator() { return m_numerator; } int getDenominator() { return m_denominator; } double getValue() { return static_cast<double>(m_numerator) / m_denominator; } };

Making default constructor with parameters

When implementing your constructors, consider how you might keep the number of constructors down through smart defaulting of values. #include <cassert> class Fraction { private: int m_numerator; int m_denominator; public: // Default constructor Fraction(int numerator=0, int denominator=1) { assert(denominator != 0); m_numerator = numerator; m_denominator = denominator; } int getNumerator() { return m_numerator; } int getDenominator() { return m_denominator; } double getValue() { return static_cast<double>(m_numerator) / m_denominator; } }; Fraction zero; // will call Fraction(0, 1) Fraction zero{}; // will call Fraction(0, 1) Fraction six{ 6 }; // will call Fraction(6, 1) Fraction fiveThirds{ 5, 3 }; // will call Fraction(5, 3)

Why make member variables private?

With a fully encapsulated class, you only need to know what member functions are publicly available to use the class, what arguments they take, and what values they return. It doesn't matter how the class was implemented internally. For example, a class holding a list of names could have been implemented using a dynamic array of C-style strings, std::array, std::vector, std::map, std::list, or one of many other data structures. In order to use the class, you don't need to know (or care) which. This dramatically reduces the complexity of your programs, and also reduces mistakes. More than any other reason, this is the key advantage of encapsulation.

Member functions vs non-member functions (Need of forward declaration)

With normal non-member functions, a function can't call a function that's defined "below" it (without a forward declaration): void x() { // You can't call y() from here unless the compiler has already seen a forward declaration for y() } void y() { } With member functions, this limitation doesn't apply: class foo { public: void x() { y(); } // okay to call y() here, even though y() isn't defined until later in this class void y() { }; };

The group of public members of a class are often referred to as

a public interface.

Setters (also sometimes called mutators)

are functions that set the value of a private member variable.

Use of type alias in the class

class Calculator { public: using number_t = int; // this is a nested type alias std::vector<number_t> m_resultHistory{}; number_t add(number_t a, number_t b) { auto result{ a + b }; m_resultHistory.push_back(result); return result; } }; When we decide that an int no longer fulfills our needs and we want to use a double, we only need to update the type alias, rather than having to replace every occurrence of int with double. nested types should only be used when the nested type is used exclusively within that class.

the implicit object.

class DateClass { public: int m_year; int m_month; int m_day; void print() { std::cout << m_year << "/" << m_month << "/" << m_day; } }; What do m_year, m_month, and m_day actually refer to? They refer to the associated object (as determined by the caller). So when we call "today.print()", the compiler interprets m_day as today.m_day, m_month as today.m_month, and m_year as today.m_year. If we called "tomorrow.print()", m_day would refer to tomorrow.m_day instead. In this way, the associated object is essentially implicitly passed to the member function.

Calling member function inside a class, changing variable in class

class DateClass { public: int m_year; int m_month; int m_day; void print() { std::cout << m_year << "/" << m_month << "/" << m_day; } }; int main() { DateClass today { 2020, 10, 14 }; today.m_day = 16; // use member selection operator to select a member variable of the class today.print(); // use member selection operator to call a member function of the class return 0; }

Can functions from one instance of the class, access another instance of the same class

class DateClass // members are private by default { int m_month; // private by default, can only be accessed by other members int m_day; // private by default, can only be accessed by other members int m_year; // private by default, can only be accessed by other members public: void copyFrom(const DateClass &d) { // Note that we can access the private members of d directly m_month = d.m_month; m_day = d.m_day; m_year = d.m_year; } }; int main() { DateClass date; date.setDate(10, 14, 2020); // okay, because setDate() is public DateClass copy; copy.copyFrom(date); // okay, because copyFrom() is public copy.print(); return 0; } One nuance of C++ that is often missed or misunderstood is that access control works on a per-class basis, not a per-object basis. This means that when a function has access to the private members of a class, it can access the private members of any object of that class type that it can see.

Setting private member variables of class

class DateClass // members are private by default { int m_month; // private by default, can only be accessed by other members int m_day; // private by default, can only be accessed by other members int m_year; // private by default, can only be accessed by other members public: void setDate(int month, int day, int year) // public, can be accessed by anyone { // setDate() can access the private members of the class because it is a member of the class itself m_month = month; m_day = day; m_year = year; } }; DateClass date; date.setDate(10, 14, 2020); // okay, because setDate() is public

The problem of constructors and const variables

class Something { private: const int m_value; public: Something() { m_value = 1; // error: const vars can not be assigned to } };

Initializign variables in constructor(not assigning them)

class Something { private: int m_value1; double m_value2; char m_value3; public: Something() : m_value1{ 1 }, m_value2{ 2.2 }, m_value3{ 'c' } // Initialize our member variables { // No need for assignment here } };

Initializing(not assigning) variables in constructor with arguments

class Something { private: int m_value1; double m_value2; char m_value3; public: Something(int value1, double value2, char value3='c') : m_value1{ value1 }, m_value2{ value2 }, m_value3{ value3 } { // No need for assignment here } void print() };

Creating multiple constructors to handle different ways of initialization

class Something { public: // Default constructor Something(int n = 0, double d = 1.2) // allows us to construct a Something(int, double), Something(int), or Something() { } Something(double d) { } }; int main() { Something s1 { 1, 2.4 }; // calls Something(int, double) Something s2 { 1 }; // calls Something(int, double) Something s3 {}; // calls Something(int, double) Something s4 { 2.4 }; // calls Something(double) return 0; }

Restriction of default parameters

class Something { public: // Default constructor Something(int n = 0, double d = 1.2) // allows us to construct a Something(int, double), Something(int), or Something() { } }; Something s4 { 2.4 }; // will not compile, as there's no constructor to handle Something(double) With s4, we've attempted to construct a Something by providing only a double. This won't compile, as the rules for how arguments match with default parameters won't allow us to skip a non-rightmost parameter (in this case, the leftmost int parameter).

If your class has no constructors, C++ will automatically generate a public default constructor for you. This is sometimes called

implicit constructor (or implicitly generated constructor). class Date { private: int m_year{ 1900 }; int m_month{ 1 }; int m_day{ 1 }; // No constructor provided, so C++ creates a public default constructor for us }; int main() { Date date{}; // calls implicit constructor return 0; } This particular implicit constructor allows us to create a Date object with no arguments, but doesn't initialize any of the members (because all of the members are fundamental types, and those don't get initialized upon creation). If Date had members that are class-types themselves, for example std::string, the constructors of those members would be called automatically. To make sure the member variables get initialized, we can initialize them at their declaration.

C++ provides 3 different access specifier keywords:

public, private, and protected. Public and private are used to make the members that follow them public members or private members respectively. The third access specifier, protected, works much like private does. We will discuss the difference between the private and protected access specifier when we cover inheritance.

Explicitly defaulted constructor vs Empty user-provided constructor

public: // Explicitly defaulted constructor Date() = default; public: // Empty user-provided constructor Date2() {}; Using = default is almost the same as adding a default constructor with an empty body. The only difference is that = default allows us the safely initialize member variables even if they don't have an initializer:


Set pelajaran terkait

Group & Team Quiz 2: Chp 3,16, 17

View Set

(Q's) from (Iggy, Pharm Success, NLEX, Medsurg success and AEQ)

View Set