类的基本思想是数据抽象和封装。数据抽象是一种依赖于接口和实现分离的编程。类的接口包括用户能执行的操作;类的实现包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数。
本文梳理一下类的基础知识和一些值得注意的点,参考《C++ Primer》第七章。
类中包括成员变量和成员函数,可以定义为 private 或者public。
比如定义一个Person类
class Person{
int height;
int age;
...
public:
int get_height(){return height;}
...
}
这里,public之前的成员都是私有成员 ,类内可见,类外不可见。public之后的成员开放给类外访问,提供给用户一个使用此类的接口。
C++中定义一个类有struct和class两种类型,它们的区别在于默认的访问权限上。在第一个访问说明符前,struct默认是public,class默认是private。
虽然其数据成员是private的,但是类可以通过声明其他类或者函数为其友元,使得这些友元可以访问类的私有成员。
这里的友元可能有以下几种情况:
- 作为类的接口之一的非成员函数其他类其他类中的成员
这里需要注意的有几点,
一是声明其他类中的成员为友元时,要写清楚名字作用域,另一点在于,必须注意定义和声明之间的依赖关系。友元仅仅提供了访问权限,并不是真正的声明。
比如如下的例子:
struct X{
friend void f();//提供访问权限,f()尚未声明
X() {f();} //错误,此时f()并未声明
void g();
void h();
}
void X::g() {return f();} //f() 未声明
void f();
void X::h() {return f();}//正确,f的声明在作用域中了
c++中对于类的编译有如下的顺序:
- 首先编译成员声明(不包括函数体),使得类中的所有名字完全可见;编译函数体
此外,c++中名字查找的顺序是:
- 对应块中查找,只考虑名字使用前出现的声明;若没有找到,继续查早外层作用域;查找不到,程序报错。
所以,对于如上的例子来说,X中的x()函数在类内的定义,由于无法查找到f()的声明,因此是错误的,上面的友元声明仅仅提供了访问权限,并不是真正的声明。直到f()被声明了,类外的x()定义才是有效的。
综上,在声明其他类的成员函数为友元时,要严格注意声明和定义的依赖关系。
2 this那些事 this是类中的隐式参数c++类中的成员函数,有的看似没有参数,实际上有隐含的参数。比如回到上面的person类:
class Person{
int height=180;//类内初始值
int age=20;
...
public:
int get_height(){return height;}
...
}
int main(){
Person A;
cout<
这里,在调用A的成员函数get_height()时,看似get_height()是一个无参数的函数,实际上执行过程为:
get_height(&A)
成员函数使用一个叫this的隐式参数来访问调用它的那个对象。调用成员函数时,用对象的地址初始化this。在成员函数内部可以直接调用函数对象成员,这种直接访问被看作this的隐式引用。实际上get_height()也可以写成如下形式:
int get_height(){return this->height;}
this默认情况下是指向类非常量版本的常量指针
比如在上面的person类中,this的默认类型是person *const。this的初始化同样需要遵循初始化规则,所以默认情况下this不能被绑定到常量对象上。因此,对于常量对象,它不能调用普通成员函数。
如果想要使this声明为指向常量的指针,可以将const关键字放在成员函数的参数列表之后,这种成员函数称为常量成员函数。
person::get_height() const {return height;}
//可以理解为
person::get_height() (const person *const this) {return this->height;}
返回*this的成员函数
返回引用和非引用的区别
返回引用,则返回一个左值,对该返回值的操作可以直接改变对象
不是返回引用,则返回的是一个临时值副本
class Screen {
public:
Screen &set(char);
Screen &set(pos,pos,char);
}
inline Screen &Screen::set(char c){
contents[cursor] = c;
return *this;
}
inline Screen &Screen:set(pos r, pos l,char ch){
contents[r*width+col] = ch;
return *this;
}
调用上述函数
myScreen.move(4,0).set('#');//move返回左值,可修改
而如果不是返回一个引用,则返回的是一个副本、右值。
#include
using namespace std;
class person{
int height=180;
public:
person set_height(int _height){
height=_height;
return *this;
}
void print_height(){
cout<
上述函数调用后会显示:
test.cpp:20:36: error: taking address of rvalue [-fpermissive]
20 | cout<<(int) &(A.set_height(190))<
const成员函数返回的是常量引用
假如函数display是常量成员函数,则下面的操作是错误的
Screen myScreen;
myScreen.display(cout).set('*');
虽然mySreen不是常量对象,但是display返回的是常量引用



