class className
{
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
类中的元素称为类的成员:类中的数据称为类的属性或者成员变量; 类中的函数称为类的方法或者成员函数。
定义类的关键字有两个:
class : class定义的类,成员访问权限默认为private
struct :struct定义的类,成员访问权限默认为public
【注意】:
1、类中成员函数声明和定义分开实现时,函数的定义需要通过域作用限定符::找到指定的类。指定类和::写在函数名前面,而不是返回类型前。
2、声明定义同时在类里面实现的函数会被默认加上inline。
面向对象的三大特性:封装、继承、多态。
封装:隐藏对象的属性和方法的实现细节,仅通过对外公开接口来和对象进行交互。
C++实现封装的方式:用类将对象的属性与方法结合在一起,通过访问权限选择性地将接口提供给外部的用户使用。
【访问限定符说明】:
类的作用域:类定义了一个新的作用域,类的所有成员都在类的作用域中。在类外面定义成员,需要使用 :: 作用域解析符指明成员属于哪个类域。
类的实例化 <=> 定义一个类对象 =>才开辟空间
类写出来后没有实例化定义对象时仅仅是个声明,没有开辟空间,所以不能直接使用类里public的变量和函数。 类就像是设计房子的图纸,实例化就是按照图纸去建造房子。
类中的成员变量储存在对象中,成员函数则储存在公共的代码段,静态成员储存在静态区也不在对象中。即,对象中只存储非静态成员变量。
对象所占的内存大小: 类中非静态成员变量经内存对齐后的大小。
【注意】:空类的大小为1字节,编译器给了空类一个字节来唯一标识这个类。
C++编译器给每个非静态成员函数都增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作都是通过该指针去访问,这个指针就是this指针。
this指针的特性:
this指针:类类型* this ,每个非静态成员函数的第一个形参都是隐含的this指针;
this指针本质上是成员函数的一个形参,是对象调用成员函数时将对象地址作为实参传递给this形参,所以this指针不存储在对象里,而是存在函数的栈帧中;
this指针不能在形参中显示写出来,也不能实参显示传递,一般都是由编译器通过寄存器自动传递,但是可以在函数内显示使用。
由于this指针的存在,我们知道了成员函数(以下均指非静态成员函数)为什么只能通过对象才能够调用,而成员函数也是对对象进行处理,this指针的存在体现了C++面向对象的特点,同时也实现了封装。
每个类中都存在6个默认成员函数,这些默认成员函数我们不能主动实现时编译器会自动生成。
构造函数的主要任务是初始化对象,而不是创建对象。
构造函数的特性:
class Date
{
public:
// 1.无参构造函数
Date()
{}
// 2.带参构造函数
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
编译器自动生成的构造函数对内置类型不做处理,对自定义类型会去调用它自己的构造函数。
初始化列表: 以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个成员变量后面跟一个放在括号中的初始值或表达式。
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
初始化列表是对象成员定义的位置。构造函数建议均使用初始化列表初始化对象,而函数体内部可以用来做检查,赋值等列表干不了的活。
【注意】:
构造函数的实现及使用:
默认构造函数: 不传参就可以调用的构造函数,包括:无参构造函数,全缺省构造函数和编译器自动生成的构造函数。一个类中只能有一个默认构造函数。
【注意】:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给缺省值。
析构函数是在对象销毁时会自动调用去完成对象中资源的清理工作。
析构函数的特性:
默认析构函数对内置类型不做处理,对自定义类型会去调用它的析构函数,不过没有动态申请内存内置类型本来就不用处理,会随着栈区销毁而释放。
析构函数的实现:
~Stack() //示例
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
拷贝构造函数:用于同类对象初始化创建对象。
浅拷贝:按内存存储的字节序完成拷贝。
拷贝构造函数的特性:
C++语法规定了,自定义类型对象的拷贝必须要调用拷贝构造来完成拷贝。若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对内置类进行浅拷贝,对自定义类型会去调用它的拷贝构造。
// Date(const Date d) // 错误写法:编译报错,会引发无穷递归
Date(const Date& d) // 正确写法
{
_year = d._year;
_month = d._month;
_day = d._day;
}
String(const String& s) //拷贝构造 -- 深拷贝
: _str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
}
拷贝构造函数典型调用场景:
C++引入了运算符重载,运算符重载是具有特殊函数名的函数,同样具有返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名为:关键字operator后面接需要重载的运算符符号。
函数声明:返回值类型 operator操作符(参数列表);
【注意】:
.* :: sizeof ?: . 以上5个运算符不能重载。
// 这里需要注意的是,左操作数是this,指向调用函数的对象
bool operator==(const Date& d) // bool operator==(Date* this, const Date& d)
{
return _year == d._year;
&& _month == d._month
&& _day == d._day;
}
赋值运算符重载格式:
Date& operator=(const Date& d) //示例
{
if(this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
【注意】:赋值运算符只能重载成类的成员函数不能重载成全局函数,因为类内未显式实现编译器还会自动在生成一个,就会冲突。
// 前置++
// 注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率
Date& operator++()
{
_day += 1;
return *this;
}
// 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
// C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递
// 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this+1
//而temp是临时对象,因此只能以值的方式返回,不能返回引用
// 后置++:
Date operator++(int)
{
Date temp(*this);
_day += 1;
return temp;
}
前置- - 和 后置- - 同理。