Programming in C++

Introduction to Object Oriented Programming (OOP)

Structs (Composite Data Types)

Gerald Senarclens de Grancy

What are C++ Structs

C++ structs are, in terms of functionality, a superset of C structs

C++ structs allow to store multiple variables (data members) along with behavior (member functions)

C++ structs allow encapsulation and inheritance

The only difference between struct and class in C++ are

  • access specifier defaults to public for struct and private for class
  • inheritance defaults to public for struct and private for class

Advantages of C++ Structs

C++ structs allow real OOP including advanced concepts such as encapsulation and inheritance

Disadvantages of C++ Structs

In C++ struct and class are (besides their default access specifier) identical which can lead to confusion

It is recommended to use struct in C++ similar to the much more limited struct in C

C++ Core Guidelines

  • Use struct if the data members can change independently

Google C++ Style Guide

  • Use a struct only for passive objects that carry data
  • The struct must not have invariants that imply relationships between different fields

Defining a C++ struct

struct StructName {
  StructName();  // constructors are supported
  ~StructName();  // destructors are supported

  type member1;
  type member2;
  /* declare as many members as desired */

  type method1(param1, ...);
  type method2(.);
  /* declare as many methods as desired */
};

Example: struct Point

#include <iostream>
struct Point {
  int x = 0;  // default values are allowed (and recommended) since C++11
  int y = 0;
};
int main() {
  Point p;  // Create Point object (instance); initialize with default values
  std::cout << "x: " << p.x << ", y: " << p.y << std::endl;
  Point p2{ .x = 5, .y = 3 };  // Create another instance with given values
  p2.x = 4;  // members are public by default
  std::cout << "x: " << p2.x << ", y: " << p2.y << std::endl;
  return 0;
}

Visualize execution

A C++ struct can have methods

Data fields are directly available

Example: Member Functions

#include <iostream>
struct Point {
  int x = 0;  // default values are allowed since C++11
  int y = 0;
  void print() {
    std::cout << "x: " << x << ", y: " << y << std::endl;
  }
};
int main() {
  Point p{ .x = 5, .y = 3 };  // initialize with given values
  p.print();
  return 0;
}

Visualize execution

Member access can be explicit using the this pointer

Example: this

#include <iostream>
struct Point {
  int x = 0;
  int y = 0;
  void print() {
    std::cout << "x: " << (*this).x << ", y: " << this->y << std::endl;
  }
};
int main() {
  Point p{ .x = 5, .y = 3 };  // initialize with given values
  p.print();
  return 0;
}

Visualize execution

Constructor and Destructor

In OOP, a constructor (abbreviation: ctor) is a special type of method called to create an object

A destructor (sometimes abbreviated dtor) is a method which is invoked just before the memory of the object is released

Example: Constructor for Point

#include <iostream>
struct Point {
  int x = 0;  // default values are allowed since C++11
  int y = 0;
  Point(int x, int y) : x(x), y(y) {}  // constructor with initializer list
  Point() : Point(0, 0) {}  // Point() delegates to Point(0, 0)
  void print() {
    std::cout << "x: " << x << ", y: " << y << std::endl;
  }
};
int main() {
  Point p1;  // same as `Point p1 = Point()`
  Point p2{-1, 2}; // use constructor with arguments
  Point p3(5, 3);  // alternative to `Point p3 = Point(5, 3)`
  p2.print();
  return 0;
}

Visualize execution

Free Store

In C++, the free store can be used with the new and delete keywords.

#include <iostream>

struct Point {
  int x = 0;
  int y = 0;
  Point(int x, int y) : x(x), y(y) {}  // constructor with initializer list
  Point() = default;  // use compiler generated default constructor
  void print() {
    std::cout << "x: " << x << ", y: " << y << std::endl;
  }
};
int main() {
  Point* p = new Point(5, 3);  // create object on the free store
  p->print();
  delete p;  // free the reserved memory
  return 0;
}

Visualize execution

Separate Declaration and Definition

To use a struct in other files, declare it in a header file

Structs are usually also defined in header files

Struct methods are declared in the struct definition but defined in a designated source file

C++ Core Guidelines

  • Explicit distinction between interface (*.hpp) and implementation (*.cpp) improves readability and simplifies maintenance

Example: Header File Contains the API

#ifndef POINT_HPP
#define POINT_HPP

namespace draw {
struct Point {
  int x;
  int y;
  Point(int x, int y) : x(x), y(y) {}; // constructor with initializer list
  Point() = default;  // we don't define anything - no need to do this in .cpp
  void print();  // method is declared, not defined
};
}

#endif
Download point.hpp

Example: Source File

#include "point.hpp"
#include <iostream>

namespace draw {

// Point::Point(int x, int y) : x(x), y(y) {};  // struct scope would be needed

void Point::print() {  // method within struct scope
  std::cout << "x: " << x << ", y: " << y << std::endl;
}

}  // namespace draw
Download point.cpp

Example: Use the struct

The struct can be used from any other file

#include "point.hpp"

int main(void) {
  draw::Point p1{3, 4};  // create instance of Point from namespace draw
  draw::Point p2{5, 0};  // create another object (instance)
  p1.print();
  p2.print();
}
Download main.cpp

Compile the program with clang++ main.cpp point.cpp

Key Differences from C

In C++, struct is much more powerful than in C

  • C++ struct can have member functions (methods)
  • C++ struct can have constructors and destructors
  • this keyword is a reference to the current instance
    this is implicitly passed to every method
  • struct keyword not needed when creating an instance
    ⇒ no need to typedef a simple struct
  • C++11 allows default values for struct properties

C++ structs are even more powerful, but it is best practice to resort to use classes instead when those features are needed

Questions
and feedback...