Files
NoteNextra-origin/content/CSE332S/CSE332S_L8.md
2025-07-06 12:40:25 -05:00

7.1 KiB
Raw Blame History

CSE332S 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 objects 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 class
  • protected: also visible within derived classes (more later)
  • public: visible everywhere

Access control in a class is private by default

  • Its 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 others internal state
  • But, other classes should not have the same direct access
  • Containers and iterators are an example of this

We could:

  1. Make the internal state public, but this violates encapsulation
  2. Use an inheritance relationship and make the internal state protected, but the inheritance relationship doesnt make sense
  3. 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;
}