Classes and Structs
Classes are user-defined types that aggregate data and functions. When we instantiate a class, we are creating an instance (or member) of that class.
- Example of a class:
Date - Example of a class instance:
today,bookingDate
The "data" of a class are called data members or instance variables. These usually represent the "properties" of the class. For example, a Book probably has a title, an author, etc. Each class members have their own data member values.
The "functions" of a class are called member functions or methods. These are usually functions to obtain or manipulate the "data" of the class.
Defining classes
Use struct for plain-old data (POD) classes
Plain-old-data (POD) classes are classes that only contains "data" (no "functions").
POD classes are usually defined using struct. (You can also define functions inside struct, but you kind of break the convention.)
struct Date {
int day;
int month;
int year;
};
int main() {
// Instantiate date
Date today;
// Set date data
today.date = 1;
today.month = 1;
today.year = 2020;
}
Use class for fully featured classes
When you need a class that has "data" and "functions," you can use class.
class Counter {
private:
// You can initialize directly
int count = 0;
public:
void setCount(int newCount) {
count = newCount;
}
void increment() {
count++;
}
};
int main() {
// Instantiate counter
Counter counter;
// Modify counter data
counter.increment();
counter.increment();
printf("%d\n", counter.getCount()); //=> 2
}
Access specifiers
Access specifiers specify who can access a class' data or functions. (This prevents external code from directly modifying internal data of a class.)
Kinds of access specifier
public— the class' data or functions can be accessed everywhereprivate— the class' data or functions can only be accessed (1) inside the class or (2) inside its friend classesprotected— the class' data or functions can only be accessed (1) inside the class, (2) inside its friend classes, (3) inside its subclasses, or (4) inside its friend classes' subclasses
Access specifier example
In the Counter class below,
countis privategetCount(),setCount(), andincrement()are public
class Counter {
private:
int count = 0;
public:
int getCount() {
return count;
}
void increment() {
count++;
}
};
Trying to do the following will cause compilation error:
Counter counter;
// ERROR: 'count' is a private member of 'Counter'
counter.count = 0;
Default access specifier
- In a
struct, the default access specifier isprivate - In a
class, the default access specifier ispublic
Constructors
Constructors are functions that are called whenever an object is created (instantiated). You can use constructors to initialize data members of a class.
You define a constructor using the name of the class, e.g.
MyClass().Constructors can take any number of arguments.
You can have multiple constructors as long as their argument count and/or type differ.
If you do not define any constructor, the compiler will implicitly define a public default constructor for you (that does nothing)
class Counter {
private:
int count;
public:
// Constructor 1
Counter() {
count = 0;
}
// Constructor 2
Counter(int initCount) {
count = initCount;
}
int getCount() {
return count;
}
void increment() {
count++;
}
};
// This will call the argument that accepts nothing
Counter counter_a = {};
printf("%d\n", counter_a.getCount()); //=> 0
// This will call the argument that accepts one int
Counter counter_b = {5};
printf("%d\n", counter_b.getCount()); //=> 5
Destructors
Destructors are functions that are called whenever an object is about to be destroyed. You can use destructors to do some cleanup logic.
You never manually call a destructor; they are called implicitly by the compiler (for example when you exit a function, and a reference to that class' intance is no longer required).
You define a destructor using
~plus the name of the class, e.g.~MyClass().Destructor take no arguments.
If you do not define any destructor, the compiler will implicitly define a default destructor for you (that does nothing)
struct MyClass {
~MyClass() {
printf("Kaboom\n");
}
}
Constant members
Use the const keyword to prevent unintentional changes to your class' "data".
Const data member
Marking a class "data" const means it cannot be changed past initialization.
struct Scoring {
const int passing_grade = 85;
};
Const member function
Marking a class "function" const means it cannot modify the class' "data".
class Counter {
int count = 0;
public:
int getCount() const {
// If you try to assign to `count` here,
// you will get a compile error.
return count;
}
void increment() {
count++;
}
};
Static members
TODO
Initialization
{})."Part of the reason C++ initialization syntax is such a mess is that the language grew out of C, where object life cycles are primitive, into a language with a robust and featureful object life cycle." — Lospinoso
If you didn't define a constructor
These call the default constructor (the constructor that does nothing):
// These lines below mean the same
MyClass my_class;
MyClass my_class = {};
MyClass my_class {};
If all of the data members in your class is public (excluding member functions), these initialize them in order from top to bottom:
struct MyClass {
public:
int a;
bool b;
private:
void hello() {}
};
// These lines below mean the same
MyClass my_class = { 1, true };
MyClass my_class { 1, true };
If you defined a constructor
These call the constructor that accepts no arguments:
// These lines below mean the same
MyClass my_class;
MyClass my_class = {};
MyClass my_class {};
These call the corresponding constructor, given the type and order of the arguments:
// These lines below mean the same
MyClass my_class = { 1, true };
MyClass my_class { 1, true };
Only if you pass more than one argument, you can also use parentheses (although it is not recommended). (If you pass no arguments, MyClass() is considered a function call to a yet-to-be-defined function, so it produces nothing.)
MyClass my_class(1, true);
With const data members
If you specify no constructor: use braced initialization, assuming and all data members in your class are public.
struct ScoringScheme {
const int passing_grade = 85;
};
ScoringScheme scheme_a = { 90 };
printf("%d\n", scheme_a.passing_grade);
//=> 90
If you specify a constructor: use member initializer lists on the constructor.
class Pair {
const int a;
const int b;
public:
// You can also use parenthesis, e.g.:
// Pair(int init_a, int init_b) : a(init_a), b(init_b)
Pair(int init_a, int init_b) : a{init_a}, b{init_b} {
return;
}
void printValues() const {
printf("(%d, %d)\n");
}
};
Pair pair = { 1, 2 };
pair.printValues();
//=> (1, 2)
References
- C++ Crash Course (Josh Lospinoso) — 2. Types
- C++ Crash Course (Josh Lospinoso) — 3. Reference Types
- When should you use a class vs a struct in C++? — https://stackoverflow.com/a/1191880/5181368
- Access specifiers — https://en.cppreference.com/w/cpp/language/access