Class
Fully featured struct
POD struct
In the previous lectures, we’ve been using struct
to define a collection of data fields that can be of different types.
This is called Plain Old Data (POD) struct
(These classes only contain data members, no member functions), which is C-compatible.
struct Complex {
char name[20];
int born;
bool male;
char address[100];
float gpa;
};
Using POD struct
can create a lot of complexities, one of them is that we need to change the value of each member manually.
;
Student s1
(s1.name, "John");
strcpy.born = 2000;
s1.male = true;
s1(s1.address, "1234 Main St, New York, NY 10001");
strcpy.gpa = 3.5; s1
To solve this problem, we can encapsulate the data and the operations on the data together:
- You can put everything related to a particular entity together, which makes the code more organized and easier to maintain.
- You can hide the details of some operations from the user, and only expose the interface that the user needs. (Information hiding)
Fully featured struct
By adding member functions(methods) to the struct
.
struct Student {
char name[20];
int born;
bool male;
char address[100];
float gpa;
void set_name(const char* s) { // this setter can be improved
(name, s, sizeof(name) - 1);
strncpy}
// other setters are omitted
};
Like you access data members, you can access member functions through the .
operator.
;
Student s1.set_name("John"); s1
File structure
The struct/class definition and implementation are usually put in separate files.1
// Student.h
#pragma once
struct Student {
char name[20];
int born;
bool male;
char address[100];
float gpa;
void set_name(const char* s);
// other methods are omitted
};
// Student.cpp
#include "Student.h"
void Student::set_name(const char* s) {
(name, s);
strcpy}
- You have to use the scope resolution operator
::
to tell the compiler that the function belongs to theStudent
struct.
Class
Access control (Encapsulation)
Access control restricts the access to certain parts of the class.
public
and private
are two major access levels.
- Anyone can access
public
members. - Only the member functions of the class can access
private
members.
In struct
, all the members are public
if you don’t specify the access level. To make members private
, you have to explicitly specify it.
struct Student {
private:
char name[20];
int born;
bool male;
char address[100];
float gpa;
public:
void set_name(const char* s) {
if (strlen(s) < sizeof(name)) {
(name, s, sizeof(name) - 1);
strncpy[sizeof(name) - 1] = '\0';
name}
else {
<< "Name is too long!" << endl;
cout }
}
// other getters and setters are omitted
};
- Rather than allowing users of
Student
to modify thename
directly, we only expose theset_name
method. - This ensures that the
name
is not modified incorrectly.
class
keyword
In C++, people usually use class
instead of struct
to define a class.
The only difference between struct
and class
is the default access level of the members.
struct
members are public by default.class
members are private by default.
class Student {
// private members
// it's still recommended to use `private` explicitly
char name[20];
int born;
bool male;
char address[100];
float gpa;
public:
void set_name(const char* s) {
if (strlen(s) < sizeof(name)) {
(name, s, sizeof(name) - 1);
strncpy[sizeof(name) - 1] = '\0';
name}
else {
<< "Name is too long!" << endl;
cout }
}
};
You can only access private members through the member functions.
A question: then how can we initialize the member variables? See constructors below.
Recall
Aggregate initialization
Recall that in struct
, we can use aggregate initialization to initialize member variables.
struct Student {
char name[20];
int born;
bool male;
};
= {"John", 2000, true}; Student s1
Default member initializer
C++11 introduces default member initializer.
struct Student {
char name[20] = "John";
int born = 2000;
bool male = true;
};
To create a Student
object without using the default values, you can use the following ways2 3:
// Student s1 = {"David", 2001, true}; // error
{"David", 2001, true}; // c++11, list initialization
Student s2 = Student {"Jane", 2002, false}; // copy-list-initialization
Student s3 = Student {.name="Eric"}; // c++20, designated initialization Student s4
Constructors
Constructor
Constructor is a special method with special declaration.
- Name of the constructor is the same as the name of the class.
- No return type is needed.
- A constructor will be invoked automatically when an object is created.
In our previous examples, we haven’t written a constructor, so the compiler will automatically generate a default constructor with empty body.
Example
class Student {
private:
;
string nameint born;
bool male;
;
string addressfloat gpa;
public:
/* Constructor:
1. No return type
2. Name is the same as the class name */
(string name_, int born_, bool male_, string address_, float gpa_) {
Student= name_;
name = born_;
born = male_;
male = address_;
address = gpa_;
gpa };
};
Constructor initialization list
Initialization lists are an alternative technique for initializing an object’s data members in a constructor.
(string name_, int born_, bool male_, string address_, float gpa_) :
Student(name_), born(born_), male(male_), address(address_), gpa(gpa_) {} name
Prefer member initializer lists over assigning values in the body of the constructor.
- A member initializer list directly initializes the members.
- Assigning values in the constructor body creates a temporary object and then copies it to the member variable, which is less efficient.
Destructors
Destructor
An object’s destructor is its clean-up method.
- A destructor will be invoked automatically before an object is destroyed.
- Be formed from the class name with a
~
prefix. - No return type, no parameters.
class Student {
private:
;
string nameint born;
bool male;
// ... other members and methods ...
public:
~Student() {
<< "Destructor called for " << name << endl;
cout }
};
In practice, you don’t need to write a destructor for an object that has no dynamic memory allocation. Variables on stack are automatically destroyed when they are out of scope.
The next example shows a case where you need to write a destructor.
// avoid namespace pollution
namespace myvec {
class vector {
private:
int* data;
int size;
public:
(int size) : size(size) {
vector= new int[size];
data }
~vector() {
delete[] data;
}
// ... other methods ...
};
}
this
pointer
Introduction
In C++, every non-static member function has an implicit parameter this
pointer.
void Student::setName(const char* s) {
(name, s, sizeof(name) - 1);
strncpy}
is equivalent to
void setName(const char* s) {
(this->name, s, sizeof(this->name) - 1);
strncpy}
this
pointer is a constant pointer to the object for which the member function is called.
Why this
pointer?
The use of this
allows all instances of a class to share the same function code, rather than each object having its own copy of member functions.
- All the member variables are allocated contiguously in memory.
- Should methods be stored in the object or separately?
- To reduce the memory usage, methods are usually stored separately from the object.
- Then how does the method know which object it is supposed to operate on?
= Student {"Amy", 2000, false};
Student s1 = Student {"Bob", 2001, true};
Student s2
.setName("Bob"); s2
- With
this
pointer, the compiler knows which object is calling the method, so it can correctly modify the corresponding object.this
points to the object that called the method.
this
pointer can also avoid ambiguity in codes with the same name.
void setBorn (int b){
= b;
born }
Equivalent to:
void setBorn (int born){
this->born = born;
}
const
and static
const
variables
In previous lectures, we’ve seen that const
can be used to declare a constant variable or used in function parameter.
const int a = 10;
const int *p_int = &a;
int * const p_int2 = &a;
void func(const int *p);
void func2(const int &ref);
const
member variables
In class, const
can also be used to declare a constant member variable.
const
member variables is similar to const
variables:
- It must be initialized with a value at the time of object creation.
- It can’t be modified through the member functions.
To initialize a const
member variable, you can use the initialization list.
class MyClass {
private:
const int constVar;
public:
(int v) : constVar(v) {}
MyClass};
Alternatively, you can use default member initializer.
class MyClass {
private:
const int constVar = 10;
};
const
member functions
A const
member function is a member function that promises not to modify the object on which it is called.
const
is added after the member function’s parameter list.- The member function can’t modify the object’s member variables.
class Student {
private:
;
string namepublic:
() const {
string get_namereturn name;
}
};
static
member variables
static
member variables are shared by all instances of the class.
class Student {
private:
static size_t student_total; // declaration only
char *name;
int born;
bool male;
public:
(const char *initName, int initBorn, bool isMale) {
Student++;
student_total= new char[1024];
name (initName);
setName= initBorn;
born = isMale;
male << "Constructor: Person(const char, int , bool): student_total = "
cout << student_total << endl;
}
~Student() {
--;
student_total<< "To destroy object: " << name;
cout << ". Then " << student_total << " students are left" << endl;
cout delete[] name;
}
// .. other methods ..
};
// definition of static member variable
size_t Student::student_total = 0;
static
member variables are not part of the individual objects, but shared by all objects of the class.- They should be defined outside the class.
- They can be accessed through the class name.
A static data member may be declared inline.
- An inline static data member can be defined in the class definition. It does not need an out-of-class definition(C++17 and later).
struct Student {
inline static int student_total = 0;
// other members are omitted
};