1 类基础
一个类是一个用户自定义的类型,通常是一组成员构成,最常见的成员类别是数据成员和成员函数。成员函数可以定义初始化、拷贝、移动和析构(清理)等语义,对对象使用.操作来访问成员,对指针使用->来访问成员。一个类包含一个成员的名字空间,public成员提供类的接口,private成员提供实现的细节,struct是成员默认为public的class。
class X{
private:
int m;
public:
X(int i=0) :m{i} {} //构造函数
int mf(int i) //成员函数
{
int old=m;
m=i;
return old;
}
};
X var {7};
int user(X var,X* ptr)
{
int x=var.mf(7);
int y=var->mf(7);
int z=var.m; //错误,不能访问私有成员。
};
1.1 成员函数
struct结构下:
struct Date{
int d,m,y;
void init(int dd,int mm,int yy); //初始化
void add_year(int n); //增加n年
void add_month(int n); //增加n月
void add_day(int n); //增加n天
};
由于不同结构可能有同名成员函数,在定义成员函数时必须指定结构名:
void Date::init(int dd,int mm,int yy)
{
d=dd;
m=mm;
y=yy;
};
1.2 默认拷贝
Date d1=my_birthday;
Date d2 {my_birthday};
类似的,类对象也可以通过赋值操作拷贝.
void f(Date& d)
{
d=my_birthday;
}
默认拷贝语义是逐成员复制。
1.3 访问控制
标签public将类主体分为2部分,第一部分的名字是私有(private)的,它们只能被成员函数使用;第二部分是公有(public)的,构成类对象的公共接口。没有声明之前,类成员和成员函数默认是私有的,且非成员函数禁止使用私有成员。
1.4 class和struct区别
class X{…};根据定义,struct就是一个成员默认为公有的类,即struct S{ /…/ };就是下面定义的简写
class S{ public /*...*/ };
class Date1{
int d,m,y; //默认私有
public:
Date1(int dd,int mm,int yy);
void add_year(int n);
};
struct Date2 {
private:
int d,m,y;
public:
Date2(int dd,int mm,int yy);
void add_year(int n);
};
事实上,Date1和Date2是等价的,除了名字不一样外。C++并不要求在类定义中首先声明数据,实际上,将数据成员放在最后以强调提供公共用户接口的函数是很有意义的。
class Date3{
public:
Date3(int dd,int mm,int yy);
void add_year(int n);
private:
int d,m,y;
};
在一个类声明中可以多次使用访问说明符。
class Date4{
public:
Date4(int dd,int mm,int yy);
private:
int d,m,y;
public:
void add_year(int n);
};
1.5 构造函数
允许程序员声明一个函数,显式表明自己是专门完成对象初始化任务的。由于这种函数本质是构造一个给定类型的值,称为构造函数。构造函数的显著特征是与类具有相同的名字。
class Date1{
int d,m,y; //默认私有
public:
Date1(int dd,int mm,int yy); //构造函数
void add_year(int n);
//....
};
//构造函数初始化参数:
Date today=Date (23,6,1983);
Date xmas (25,3,1993);
Date today=Date {23,6,1983};
Date xmas {25,3,1993};
构造函数的重载规则与普通函数相同,只要构造函数的参数类型明显不同,编译器就能选择正确的版本使用。
1.6 explicit构造函数
我们可以指明构造函数不能用作隐式类型转换,如果构造函数的声明带有关键字explicit,则它只能用于初始化和显式类型转换。用=进行初始化可看作拷贝初始化,一般来说这种拷贝可能会被优化掉,而采取移动操作。省略=将会变为显式初始化,显式初始化又称直接初始化。
struct X{
explicit X();
explicit X(int,int);
};
X x1={}; //错误,隐式的;
X x2={1,2}; //错误,隐式的;
X x3 {}; //正确,显式的;
X x4 {1,2}; //正确,显式的;
int f(X);
int i1=f({}); //错误,隐式的;
int i2=f({1,2}); //错误,隐式的;
int i3 f({}); //正确,显式的;
int i4 f({1,2}); //正确,显式的;
1.7 类内函数定义
如果一个函数不仅在类中声明,还在类中定义,那么它就被当做内联函数处理。
class Date{
public:
void add_month(int n) {m+=n;};
//....
private:
int d,m,y;
};
等价于:
class Date{
public:
void add_month(int n) {m+=n;};
//....
private:
int d,m,y;
};
inline void Date::add_month(int n)
{
m+=n;
};
1.8 可变性
const和非const对象都可以调用cosnt成员函数,而非const成员函数只能被非const对象调用,const函数不能修改其数据成员。例如:
void f(Date &d,const Date &cd)
{
int i=d.year();
d.add_year(1);
int j=cd.year();
cd.add_year(1); //错误,不能改变const Date的值
};
我们可以将一个类成员定义为mutable,即使是在const对象中,也可以修改此成员。因此该函数即可用于const对象,也可以用于非const对象。
1.9 成员访问
通过对类X的对象使用.或者->操作来访问X的成员。
struct X{
void f();
int m;
};
void X::f()
{
m=1; //等价于this->m=1;
};
是类的一部分但不是某个类对象一部分的变量称为static成员,类似的,需要访问类成员而不需要通过特定对象调用的函数称为static成员函数。嵌套类能访问其所属类的成员,甚至是private成员,相反,一个类并没有任何特殊权限能访问其嵌入类的成员。
2 构造、清理、拷贝和移动
class X{
X(Sometype); //普通构造函数;
X(); //默认构造函数;
X(const X&); //拷贝构造函数;
X(X&& ); //移动构造函数;
X& operator=(const X&); //拷贝赋值运算符;
X& operator=(X&&); //拷贝赋值运算符;
~X(); //析构函数;
//...
};
2.1 构造函数和析构函数
与类同名的成员称为构造函数。构造函数的声明指出与参数列表,但未指出返回类型,类名不可用于此类内的普通成员函数、数据成员、成员类型等等。
class Vector{
public:
Vector(int s);
//...
};
某些类需要一个函数,在对象销毁时保证被调用,就像在对象创建时保证构造函数会被调用一样,这样的函数称为析构函数。析构函数不接受参数,且每一类只能有一个析构函数。
2.2基类和成员析构函数
构造函数调用其基类的构造函数,然后调用成员的构造函数,最后执行自身的函数体。析构函数执行自身的函数体,然后调用成员的析构函数,最后调用基类的析构函数。
2.3 调用构造函数和析构函数
2.4 virtual析构函数
析构函数可以声明为virtual,而且对含有虚函数的类通常这么执行。
2.5 static成员初始化
一个static类成员是静态分配,而不是每个类对象的一部分。一般来说,static成员声明充当类外定义的声明。但是在少数情况下,在类内声明中初始化static成员也是可能的,条件是static成员必须是整型或枚举类型的const,或者字面值类型的constexpr.
class Curious{
public:
static const int c1=7; //正确;
static int c2=11; //错误,非const;
const int c3=13; //正确,但非static;
static const int c4=sqrt(9); //错误,类内初始化器不是常量;
static const float c5=7.0; //错误,类内初始化器不是整型(应该使用constexpr//而非const);
};
2.6 拷贝和移动
类拷贝操作两种方式:
默认构造函数:X()
拷贝构造函数:X (const X&)
拷贝赋值运算符: X& operator=(const X&)
移动构造函数:X (X &&)
移动赋值运算符: X& operator=(X &&)
析构函数:~X()