Classes and Objects

Basic Characteristics of Object-Oriented Programming

Abstraction

  • Summarize the common attributes and behaviors of objects of the same class to form a class.
    • First focus on the essence and description of the problem, then the implementation process or details.
    • Data abstraction: Describe the attributes or state of objects of a certain class (physical quantities that distinguish objects from each other).
    • Code abstraction: Describe the common behavioral characteristics or functions that objects of a certain class have.
    • Implementation of abstraction: class.
  • Abstraction example—Clock
    • Data abstraction: int hour,int minute,int second
    • Code abstraction: setTime(),showTime()
1
2
3
4
5
6
7
class  Clock {
public:
void setTime(int newH, int newM, int newS);
void showTime();
private:
int hour, minute, second;
};

Encapsulation

  • Encapsulate the abstracted data and code together to form a class.
    • Purpose: Enhance security and simplify programming. Users don’t need to understand specific implementation details, but only need to use class members through external interfaces with specific access permissions.
    • Implementation of encapsulation: {} in class declaration
  • Example:
1
2
3
4
5
class  Clock {
public: void setTime(int newH, int newM, int newS);
void showTime();
private: int hour, minute, second;
};

Inheritance

  • Extend on the basis of existing classes to form new classes.

Polymorphism

  • Polymorphism: Same name, different functional implementations.
  • Purpose: Achieve unified behavioral identification and reduce the number of identifiers in the program.

Definition of Classes and Objects

Syntax Form of Class Definition

1
2
3
4
5
6
7
8
9
class class_name
{
public:
public members (external interface)
private:
private members
protected:
protected members
}

In-class Initial Values

  • Can provide an in-class initial value for data members
  • When creating objects, in-class initial values are used to initialize data members
  • Members without initial values will be default initialized

Class Member Access Control

  • Public type members
    • Declared after the keyword public, they are the interface between the class and the outside, any external function can access public type data and functions
  • Private type members
    • Declared after the keyword private, only functions in this class can access them, and no external function can access them.
    • If private members are declared immediately after the class name, the keyword private can be omitted
  • Protected type members
    • Similar to private, the difference is manifested in the different effects on derived classes during inheritance and derivation

Class Member Functions

  • Declare function prototypes in the class
  • Can provide function body implementation outside the class, and qualify with class name before the function name
  • Can also directly provide function body in the class, forming inline member functions
  • Allow declaration of overloaded functions and functions with default parameter values

Class and Object Program Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
using namespace std;
//class definition
class Clock{
public:
void setTime(int newH = 0,int newM = 0,int newS = 0);
void showTime();
private:
int hour,minute,second;
}
//member function implementation
void Clock::setTime(int newH = 0,int newM = 0,int newS = 0);
{
hour = newH;
minute = newM;
second = newS;
}
void Clock::showTime()
{
cout << hour << ":" << minute << ":" << second << endl;
}
//object usage
int main()
{
Clock myClock;
myClock.setTime(8,30,30);
myClock.showTime();
return 0;
}

Constructors and Destructors

Constructors

Purpose of Constructors

Use specific values to construct objects when they are created, initializing the object to a specific initial state.

When hoping to construct a Clock class object and set the initial time to 0:0:0, this can be set through a constructor.


Form of Constructors

  • Function name is the same as class name
  • Cannot define return type, and cannot have return statements
  • Can have formal parameters or no formal parameters
  • Can be inline functions
  • Can be overloaded
  • Can have default parameter values

Timing of Constructor Calls

Automatically called when objects are created


Constructor Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
using namespace std;
//class definition
class Clock{
public:
Clock(int newH,int newM,int newS);//constructor
void setTime(int newH = 0,int newM = 0,int newS = 0);
void showTime();
private:
int hour,minute,second;
};//there's a semicolon here
//constructor implementation
Clock::Clock(int newH,int newM,int newS): hour(newH),minute(newM),second(newS){}
//member function implementation
void Clock::setTime(int newH = 0,int newM = 0,int newS = 0);
{
hour = newH;
minute = newM;
second = newS;
}
void Clock::showTime()
{
cout << hour << ":" << minute << ":" << second << endl;
}
//object usage
int main()
{
Clock c(0,0,0);//automatically call constructor
c.showTime();
return 0;
}

The :: symbol is the scope resolution operator, which means function definitions need to use class_name:: to qualify member functions.

A very important thing is that there’s a semicolon after the class definition ends!!! (wasted a lot of time)


Default Constructor

  • Constructor that can be called without actual parameters
    • Constructor with empty parameter list
    • Constructor where all parameters have default values
  • The following two are both default constructors. If they appear simultaneously in a class, a compilation error will occur:
1
2
3
Clock();
Clock(int newH=0,int newM=0,int newS=0);
// Both functions require no initial values, so there will be a call conflict

隐含生成的构造函数

如果程序中未定义构造函数,编译器将在需要时自动生成一个默认构造函数

  • 参数列表为空,不为数据成员设计初始值
  • 如果类内定义了成员的初始值,则使用类内定义的初始值
  • 如果没有定义类内的初始值,则以默认方式初始化
  • 基本类型的数据默认初始化的值是不确定的

“=default”

如果类内已定义构造函数,默认情况下编译器不再隐含生成默认构造函数。 如果你坚持希望隐含生成默认构造函数,用“=default”

1
Clock() = default;//指示编译器提供默认构造函数

委托构造函数

委托构造函数(delegating constructor)使用类其他构造函数执行初始化过程

例如

1
2
3
Clock(int newH,int newM,int newS):hour(newH),minute(newM),second(newS){}
Clock():Clock(0,0,0){}
//有的编译器并不支持委托构造函数

复制构造函数

复制构造函数时一种特殊的构造函数,其形参为本类的对象引用。作用是用一个已存在的对象去初始化同类型的新对象。

用法:

1
2
3
4
5
6
7
8
9
class 类名
{
public:
类名(形参);//构造函数
类名(const 类名&对象名);//复制构造函数
// ...
};
类名::类(const 类名&对象名)//复制构造函数的实现
{函数体}

隐含的复制构造函数

  • 如果没有为类声明拷贝初始化构造函数,则比编译器自己生成一个复制构造函数。
  • 这个构造函数执行的功能是:用作为初始值对象的每个数据成员的值,初始化将要建立的对象的对应数据成员。

“=delete”

如若不希望被复制构造

1
Point(const Point&p) = delete;

复制构造函数被调用的三种情况

  • 定义一个对象是,以被雷另一个对象作为初始值,发生复制构造
  • 如果函数的形参是类的对象,调用函数时,将使用实参对象初始化形参对象,发生复制构造
  • 如果函数的返回值是类的对象,函数执行完成返回主函数时,将使用return语句中的对象初始化一个临时无名对象,传递给主函数,此时发生复制构造

析构函数

作用:完成对象被删除前的一些清理工作

  • 在对象生存期结束的时刻系统自动调用它,然后再释放此对象所属的空间
  • 如果程序中未声明析构函数,编译器会自动生成一个默认的析构函数,函数体为空

类的组合

说白了就是类的数据成员是别的类的对象,下面用计算两点之间线段的距离的程序来说明。


线段类和点类实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <cmath>
using namespace std;

//Point类
class Point{
public:
Point(float xx,float yy); //构造函数
Point(); //默认构造函数
Point(Point &p); //复制构造函数
float getX(){return x;}
float getY(){return y;}
private:
float x,y;
};
Point::Point(float xx,float yy):x(xx),y(yy){} //构造函数的定义
Point::Point():x(0),y(0){} //默认构造函数的定义
Point::Point(Point &p) //复制构造函数的定义
{
x=p.x;
y=p.y;
cout << "Calling the copy constructor of Point" << endl;
}
//Line类,类的组合,计算两点之间的距离
class Line{
public:
Line(Point o1,Point o2); //参数为Point对象的构造函数
Line(Line &l); //复制构造函数
float getLen(); //外部接口
private:
Point p1,p2;
float len=0;
};
Line::Line(Point o1,Point o2):p1(o1),p2(o2) //构造函数是每一个成员变量都要初始化的
{
float x=p1.getX()-p2.getX();
//此处并不能够直接写 p1.x - p2.x 因为x属于私有变量
// 通过两个public成员函数可以访问,相当于提供了外部接口
float y=p1.getY()-p2.getY();
len= sqrt(x*x+y*y);
cout << "Calling the constructor of Line" << endl;
}
Line::Line(Line &l): p1(l.p1),p2(l.p2) //复制构造函数是每一个成员变量都需要进行复制的 且这个'.'只能够在类的作用域内使用。
{
len=l.len;
cout << "Calling the copy constructor of Line" << endl;
}
float Line::getLen() {return len;}
int main()
{
Point myp1(1,1),myp2(4,5);
Line line(myp1,myp2); //参数传递的时候也需要复制构造哦
Line line2(line);
cout << "The length of line is " << line.getLen() << endl;
cout << "The length of line2 is " << line2.getLen() << endl;
return 0;
}


前向引用声明

遇到两个类相互引用的情况,也称为循环依赖,简单理解就是你不能使用一个在前面完全没有出现过的标识符。

1
2
3
4
5
6
7
8
9
class B;  					//前向引用声明
class A{ //A类的定义
public: //外部接口
void f(B b); //以B类对象b为形参的成员函数
};
class B{ //B类定义
public: //外部接口
void g(A a); //以A类对象a为形参的成员函数
};