public和private访问修饰符

公有私有成员

查看如下结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct DateStruct // 结构体成员默认是公有的(public
{
int month; // 默认公有, 可被任何人访问
int day; // 默认公有, 可被任何人访问
int year; // 默认公有, 可被任何人访问
};

int main()
{
DateStruct date;
date.month = 10;
date.day = 14;
date.year= 2020;

return 0;
}

在这段程序里,我们声明了一个DateStruct并直接访问它的成员来进行初始化。之所有可以这样是因为一个结构体的成员默认是公有的。公有成员是指结构体或者类中可以被外部访问的成员。因此,main函数位于DateStruct外,但我们仍可以直接访问它的成员年、月、日,因为它们是公有的。

另一方面,我们瞧一瞧下面这个类,它和DateStruct几乎相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class DateClass // 类成员默认是私有的
{
int m_month; // 默认私有, 只能被其他成员访问
int m_day; // 默认私有, 只能被其他成员访问
int m_year; // 默认私有, 只能被其他成员访问
};

int main()
{
DateClass date;
date.m_month = 10; // 出错
date.m_day = 14; // 出错
date.m_year = 2020; // 出错

return 0;
}

如果你编译这段程序,会报错。这是因为默认情况下,一个类的所有成员都是私有的。私有成员是指结构体或者类中只能被该结构体或类的内部成员访问的成员。因为main函数不是DateClass的内部成员,所以它不能够访问DateClass的内部成员。

访问修饰符

尽管类的成员默认是私有的,但我们可以通过public关键字让它变成公有的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class DateClass
{
public: // note use of public keyword here, and the colon
int m_month; // public, can be accessed by anyone
int m_day; // public, can be accessed by anyone
int m_year; // public, can be accessed by anyone
};

int main()
{
DateClass date;
date.m_month = 10; // okay because m_month is public
date.m_day = 14; // okay because m_day is public
date.m_year = 2020; // okay because m_year is public

return 0;
}

因为DateClass的成员现在是公有的,所以我们可以直接在main函数里访问了。

public关键字及随后的冒号就是一个访问修饰符。访问修饰符决定了谁可以访问修饰符后边的成员。每一个成员都会活得前面所指定的访问修饰符的访问级别(如果没指定的话,就是用默认访问修饰符)。

C++提供三种不同的访问修饰符关键字:public,private,和protected。public让被修饰的成员变得公有,private让被修饰的成员变得私有。第三个修饰符,即protected,功能跟private类似。在我们之后的章节开始学习继承的时候,我们会讨论一下private和protected修饰符到底有什么区别。

混合访问修饰符

类通常使用多种访问修饰符来设置其内部成员的访问级别。在类中使用访问修饰符并没有数量上的限制。

通常情况下,成员变量一般设置为私有,成员函数则设置为公有。我们会在下一节告诉你为什么。

规则:除非你有什么特殊的理由,不然就让成员变量私有,让成员方法公有。

观察下面的例子,它是一个同时使用了private和public访问修饰符的类。

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>

class DateClass // members are private by default
{
int m_month; // private by default, can only be accessed by other members
int m_day; // private by default, can only be accessed by other members
int m_year; // private by default, can only be accessed by other members

public:
void setDate(int month, int day, int year) // public, can be accessed by anyone
{
// setDate() can access the private members of the class because it is a member of the class itself
m_month = month;
m_day = day;
m_year = year;
}

void print() // public, can be accessed by anyone
{
std::cout << m_month << "/" << m_day << "/" << m_year;
}
};

int main()
{
DateClass date;
date.setDate(10, 14, 2020); // okay, because setDate() is public
date.print(); // okay, because print() is public

return 0;
}

这段程序输出:

10/14/2020

尽管我们不能在main函数里访问m_month、m_day、m_year(因为它们是私有的),但我们可以通过它的公有成员函数setDate()和print()对这些私有变量进行间接访问。

一个类的公有成员通常又被成为公共接口(public interface)。因为只有公公成员可以在类的外部被访问,公共接口定义了使用这个类的程序如何与这个类进行交互。注意上面的main函数,被严格限制只能设置日期和输出日期,DateClass保护它的成员变量不会被直接访问和编辑。

有些程序员喜欢把私有成员写在前面,因为公有成员一般都会使用私有成员,所以理所当然地把私有成员写在前面。然而,类的使用者并不关心私有成员,所以公有成员应该写在前面。无论哪种方式,都是可取的。

访问控制按类进行

观察下面的程序:

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
#include <iostream>

class DateClass // members are private by default
{
int m_month; // private by default, can only be accessed by other members
int m_day; // private by default, can only be accessed by other members
int m_year; // private by default, can only be accessed by other members

public:
void setDate(int month, int day, int year)
{
m_month = month;
m_day = day;
m_year = year;
}

void print()
{
std::cout << m_month << "/" << m_day << "/" << m_year;
}

// 看这里看这里
void copyFrom(const DateClass &d)
{
// 注意我们可以直接访问参数d的私有成员
m_month = d.m_month;
m_day = d.m_day;
m_year = d.m_year;
}
};

int main()
{
DateClass date;
date.setDate(10, 14, 2020); // okay, because setDate() is public

DateClass copy;
copy.copyFrom(date); // okay, because copyFrom() is public
copy.print();

return 0;
}

C++一个容易被忽视且误解的特性就是,访问控制作用于每个类,而不是每个对象(One nuance of C++ that is often missed or misunderstood is that access control works on a per-class basis, not a per-object basis.)。如果一个函数可以访问一个类的私有成员,那么这个函数就可以访问该类类型的任何对象的私有成员。

在上面的例子,copyFrom()是DateClass的一个成员,对DateClass的私有成员拥有访问权限。这意味着,copyFrom()不仅可以访问自身实例的私有成员,还可以访问参数d的私有成员!如果参数d是其他类型,那就另当别论了。

在需要从一个类的对象复制数据到同一个类的另外一个对象时,这个特性会变得非常有用。就在下一章讨论重载操作符<<来打印一个类的成员时,这个知识点我们会再次遇到。

结构体vs类复习

现在我们已经学习了访问修饰符,我们也讨论了c++当中一个类和结构体的不同之处。一个类默认它的成员都是私有的,一个结构体默认它的成员都是公有的。

那就完了!

(可是,我还想再啰嗦一下,这里还有一个细微的不同之处–结构体会公有地继承其他类,类则会私有地继承其他类。我们会在之后的章节解释它的意思,然而这一点实际上是无关紧要的,因为一般人不会使用默认继承。)