类
类实际上就是我们自己定义的一种数据类型,本质上来讲它和int,float什么的没有什么不同。
在C语言中,我们一般用struct来定义我们自己的类型,在C++中我们引入了类的概念,一般用class来定义自己的类型。实际上这两个概念很像,只不过class多了一些东西,比如可以定义private的类型等等。
构造函数
类需要一个构造函数来进行类的初始化,一般来讲,当我们new一个类的时候,会首先调用它的初始化函数来对类中的成员进行初始化。
构造函数的名字与类相同,并且没有返回值,也没有返回类型。在很多类中我们看不到构造函数,是因为编译器会隐式给类默认生成构造函数。但是在实际中我们还是应该显示的定义构造函数,这是因为有些场景下编译器生成的默认构造函数是有问题的,或是会报错的,或是在有些类中无法生成默认构造函数。
1 | class Sales_data { |
上面定义了一个类,这个类有三个构造函数。1
Sales_data() = default;
这个的意思是告诉编译器要为我们生成一个默认的构造函数,如果不这样,那么因为我们定义了构造函数,那么编译器就不会为我们生成默认的构造函数了。
剩下的两种就是我们自己定义的构造函数,这宗定义的方式我们称之为构造函数初始值列表。
当然我们还两外的构造方式,如下所示,这就跟普通的函数是一样的了。
1 | Sales_data::Sales_data(std::istream &is) { |
访问控制和封装
前面说到struct和class的不同就在于class里面可以进行访问权限控制
public:整个程序都可以使用,一般用来定义接口
private:只能被类的成员进行访问,一般用来定义类的成员以及私有的实现。
所以我们可以修正一下上面的类定义1
2
3
4
5
6
7
8
9
10
11
12class Sales_data {
public:
Sales_data() = default;
Sales_data(const std::string &s): bookNo(s) {}
Sales_data(const std::string &s, unsigned n, double p) :
bookNo(s), unit_sold(n), revenue(p) {}
Sales_data(std::istream &);
private:
std::string bookNo;
unsigned int unit_sold = 0;
double revenue = 0.0;
};
友元
由于类中的private成员不能让外部访问,所以如果我们需要外部的类或者函数来修改private成员的话,就没有办法了,因此我们定义了友元的概念。
类允许其他的类或者函数访问它的非共有成员,方法是其他类或者函数成为它的友元,用friend关键字
1 | class Sales_data { |
this
this实际上指的是类本身,在某一些场景下我们需要显示的用到this,比如我们要返回类的时候,在下面有例子说明。
类的静态成员
类的静态成员跟类本身有关,而跟它的对象基本无关。
一般用static来定义。
类的静态成员存在与任何对象之外,对象中不包含任何与静态数据成员有关的数据。静态成员也不跟任何对象绑定在一起,他也不包含this指针。
比如说定义一个账户类,那么我们可以将利率定义为static的,那么这个利率就跟类有关,而跟对象无关。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class Account {
public:
void calculate() {
amount += amount * interestRate;
}
static double rate() {
return interestRate;
}
static void rate(double);
private:
std::string owner;
double amount;
static double interestRate;
static double initRate();
};
void Account::rate(double newRate) {
interestRate = newRate;
}
在这个类中,所有的Account对象将包含两个数据成员owner和amount。但是只存在一个interestRate对象且被所有的Account对象共享。
我们可以直接用类名或者对象来访问静态成员
1 | double r = Account::rate(); |
实例说明
我们定义一个Screen类:
1 | class Screen { |
这个类里面首先我们用typedef定义了一个pos,这样就能封装类的一些细节,同时方便理解类。
内联函数
其次我们定义了内联函数,内联函数的有点就不说了,上面的例子是三种内联函数的声明方式。
mutable可变变量
1 | class Screen { |
这里我们添加了一个mutable的变量,用来表示some_member函数的调用次数。
这是因为对于函数名后加const的行为,在这个函数中是不能修改成员遍历那个的,所以必须将相应的成员变量设置为mutable。mutable是可变数据成员,当我们将一个成员变量定义为mutable时,即使这个变量在const成员函数中,我们也可以改变它的值。
1 | void some_member() const; |
this指针返回对象,用于隐式传递
我们可以从上面的代码中看到我们定义了一个函数,返回了this指针,也就是对象本身的引用。这样做是为什么呢?
1 | inline Screen &Screen::move(pos r, pos c) { |
这个函数的目的是移动屏幕的光标,那么移动完了就好了,为何还要返回对象的引用呢?这是为了解决一个本质的问题。
当我们移动一下光标后没有问题,那么当我们还需要继续操作呢?这就有问题了。
1 | Screen myScreen; |
因此返回this引用就是可以实现上面的操作。
假设我们添加了下面的display函数,实际上这个函数不需要改变任何值,那么我们可以将其设置为const类型的,如下所示:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class Screen {
public:
typedef std::string::size_type pos;
Screen() = default;
Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c) {}
const Screen &display(std::ostream &os) const {
do_display(os);
return *this;
}
private:
void do_display(std::ostream &os) const {
os << contents;
}
mutable size_t access_ptr;
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
};
但是这样有个问题,我们如果按照如下的方式调用则没有问题
1 | Screen myScreen; |
但是如下调用就麻烦了:1
2Screen myScreen;
myScreen.display(std::cout).set('*');
这是因为我们的返回值是个const,也就是myScrren是个常量,他是没有办法再进行set的。因此针对这中场景,有一种解决方式,就是基于const的重载,新添加一个非const的函数,如下所示
1 | class Screen { |
这样就解决了上面的两种调用的问题了。
Screen类的友元问题
我们定义一个Window_mgr类,这个类需要调用Screen中的私有成员,这样原则上是不允许的,要想实现,我们就需要将Window_mgr定义为Screen的友元。
1 | class Window_mgr{ |
将Window_mgr定义为Screen的友元。注意定义友元之前要先声明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
42class Window_mgr;
class Screen {
friend Window_mgr;
public:
typedef std::string::size_type pos;
Screen() = default;
Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c) {}
char get() const {
return contents[cursor]; // 隐式内联
}
Screen &move(pos r, pos c);
inline char get(pos ht, pos wd) const; // 显式内联
void some_member() const;
Screen &display(std::ostream &os) {
do_display(os);
return *this;
}
const Screen &display(std::ostream &os) const {
do_display(os);
return *this;
}
private:
void do_display(std::ostream &os) const {
os << contents;
}
mutable size_t access_ptr;
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
};
char Screen::get(pos r, pos c) const {
pos row = r * width;
return contents[row * c];
}
inline Screen &Screen::move(pos r, pos c) {
pos row = r * width;
cursor = row + c;
return *this;
}
void Screen::some_member() const {
++access_ptr;
}
也可以直接定义友元的函数,而不必要将整个类定义为友元,如下所示:
1 | friend void Window_mgr::clear(ScreenIndex); |