CSE332S Object-Oriented Programming in C++ (Lecture 8)
From procedural to object-oriented programming
Procedural programming
- Focused on functions and the call stack
- Data and functions treated as separate abstractions
- Data must be passed into/returned out of functions, functions work on any piece of data that can be passed in via parameters
Object-oriented programming
- Data and functions packaged together into a single abstraction
- Data becomes more interesting (adds behavior)
- Functions become more focused (restricts data scope)
Object-oriented programming
- Data and functions packaged together into a single abstraction
- Data becomes more interesting (adds behavior)
- Functions become more focused (restricts data scope)
Today:
- An introduction to classes and structs
- Member variables (state of an object)
- Constructors
- Member functions/operators (behaviors)
- Encapsulation
- Abstraction
At a later date:
- Inheritance (class 12)
- Polymorphism (12)
- Developing reusable OO designs (16-21)
Class and struct
From C++ Functions to C++ Structs/Classes
C++ functions encapsulate behavior
- Data used/modified by a function must be passed in via parameters
- Data produced by a function must be passed out via return type
Classes (and structs) encapsulate related data and behavior (Encapsulation)
- Member variables maintain each object’s state
- Member functions (methods) and operators have direct access to member variables of the object on which they are called
- Access to state of an object is often restricted
- Abstraction - a class presents only the relevant details of an object, through its public interface.
C++ Structs vs. C++ Classes?
Class members are private by default, struct members are public by default
When to use a struct
- Use a struct for things that are mostly about the data
- Add constructors and operators to work with STL containers/algorithms
When to use a class
- Use a class for things where the behavior is the most important part
- Prefer classes when dealing with encapsulation/polymorphism (later)
// point2d.h - struct declaration
struct Point2D {
Point2D(int x, int y);
bool operator< (const Point2D &) const; // a const member function
int x_; // promise a member variable
int y_;
};// point2d.cpp - methods functions
#include "point2d.h"
Point2D::Point2D(int x, int y) :
x_(x), y_(y) {}
bool Point2D::operator< (const Point2D &other) const {
return x_ < other.x_ || (x_ == other.x_ && y_ < other.y_);
}Structure of a class
class Date {
public: // public stores the member functions and variables accessible to the outside of class
Date(); // default constructor
Date (const Date &); // copy constructor
Date(int year, int month, int day); // constructor with parameters
virtual ~Date(); // (virtual) destructor
Date& operator= (const Date &); // assignment operator
int year() const; // accessor
int month() const; // accessor
int day() const; // accessor
void year(int year); // mutator
void month(int month); // mutator
void day(int day); // mutator
string yyymmdd() const; // generate a string representation of the date
private: // private stores the member variables that only the class can access
int year_;
int month_;
int day_;
};Class constructor
- Same name as its class
- Establishes invariants for objects of the class
- Base class/struct and member initialization list
- Used to initialize member variables
- Used to construct base class when using inheritance
- Must initialize const and reference members there
- Runs before the constructor body, object is fully initialized in constructor body
// date.h
class Date {
public:
Date();
Date(const Date &);
Date(int year, int month, int day);
~Date();
// ...
private:
int year_;
int month_;
int day_;
};// date.cpp
Date::Date() : year_(0), month_(0), day_(0) {} // initialize member variables, use pre-defined values as default values
Date::Date(const Date &other) : year_(other.year_), month_(other.month_), day_(other.day_) {} // copy constructor
Date::Date(int year, int month, int day) : year_(year), month_(month), day_(day) {} // constructor with parameters
// ...More on constructors
Compiler defined constructors:
- Compiler only defines a default constructor if no other constructor is declared
- Compiler defined constructors simply construct each member variable using the same operation
Default constructor for built-in types does nothing (leaves the variable uninitialized)!
It is an error to read an uninitialized variable
Access control and friend declarations
Declaring access control scopes within a class - where is the member visible?
private: visible only within the classprotected: also visible within derived classes (more later)public: visible everywhere
Access control in a class is private by default
- It’s better style to label access control explicitly
A struct is the same as a class, except access control for a struct is public by default
- Usually used for things that are “mostly data”
Issues with Encapsulation in C++
Encapsulation - state of an object is kept internally (private), state of an object can be changed via calls to its public interface (public member functions/operators)
Sometimes two classes are closely tied:
- One may need direct access to the other’s internal state
- But, other classes should not have the same direct access
- Containers and iterators are an example of this
We could:
- Make the internal state public, but this violates encapsulation
- Use an inheritance relationship and make the internal state protected, but the inheritance relationship doesn’t make sense
- Create fine-grained accessors and mutators, but this clutters the interface and violates abstraction
Friend declarations
Offer a limited way to open up class encapsulation
C++ allows a class to declare its “friends”
- Give access to specific classes or functions
Properties of the friend relation in C++
- Friendship gives complete access
- Friend methods/functions behave like class members
- public, protected, private scopes are all accessible by friends
- Friendship is asymmetric and voluntary
- A class gets to say what friends it has (giving permission to them)
- But one cannot “force friendship” on a class from outside it
- Friendship is not inherited
- Specific friend relationships must be declared by each class
- “Your parents’ friends are not necessarily your friends”
// in Foo.h
class Foo {
friend ostream &operator<< (ostream &out, const Foo &f); // declare a friend function, can be added at any line of the class declaration
public:
Foo(int x);
~Foo();
// ...
private:
int baz_;
};
ostream &operator<< (ostream &out, const Foo &f);// in Foo.cpp
ostream &operator<< (ostream &out, const Foo &f) {
out << f.baz_; // access private member variable via friend declaration
return out;
}