- 类的引入:C语言中,结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。
- 类的定义:类由成员函数和成员变量组成。
定义类的两种方式:声明和定义全部放在类体中;声明在.h文件中,定义在.cpp文件中。 - 访问限定符:public, private, protected
(1) public修饰的成员在类外可以直接被访问;
(2) protected和private修饰的成员在类外不能直接被访问;
(3) 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止;
(4) class的默认访问权限为private,struct为public(因为struct要兼容C). - 面向对象三大特性:封装,继承,多态。
- 类的实例化:用类类型创建对象的过程。
定义出一个类并没有分配实际的内存空间来存储它,一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量。 - 类对象大小:*对象中只存储成员变量,不存储成员函数。*因为一个类实例化出N个对象,每个对象的成员变量都可以存储不同的值,但是调用的函数却是同一个。如果每个对象都存储成员函数,而这些成员函数却是一样的,就很浪费空间。没有成员变量的类大小是1,而不是0。因为要1表示它存在。
- 结构体内存对齐:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。(对齐到某个数字(对齐数)的整数倍的地址处。)
- this指针:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。
(1) this指针的类型:类类型* const;
(2) 只能在“成员函数”的内部使用;
(3) this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针;
(4) this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递. - 类的6个默认成员函数:构造,析构,拷贝构造,赋值重载,取地址重载(普通对象和const对象)。
我们不自己实现时,编译器生成的默认构造函数和析构函数。针对成员变量:内置类型就不处理,自定义类型就会调用这个成员对象的构造函数和析构函数。 - 构造函数:不是开空间创建对象,而是初始化对象。
(1) 函数名与类名相同。
(2) 无返回值。
(3) 对象实例化时编译器自动调用对应的构造函数。
(4) 构造函数可以重载。
(5) 如果类中没有显式定义构造函数,则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 ;
};
- 析构函数:对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
(1) 析构函数名是在类名前加上字符 ~。
(2) 无参数无返回值。
(3) 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
(4) 对象生命周期结束时,C++编译系统系统自动调用析构函数。
typedef int DataType;
class SeqList
{
public :
SeqList (int capacity = 10)
{
_pData = (DataType*)malloc(capacity * sizeof(DataType));
assert(_pData);
_size = 0;
_capacity = capacity;
}
~SeqList()
{
if (_pData)
{
free(_pData ); // 释放堆上的空间
_pData = NULL; // 将指针置为空
_capacity = 0;
_size = 0;
}
}
private :
int* _pData ;
size_t _size;
size_t _capacity;
};
- 拷贝构造函数:
(1) 拷贝构造函数是构造函数的一个重载形式。
(2) 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。
(3) 若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝。
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
- 运算符重载:返回值类型 operator操作符(参数列表)
(1) 操作符有一个默认的形参this,限定为第一个形参。
(2) .* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载。
(3) 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝。
bool operator==(const Date& d1, const Date& d2) {
return d1._year == d2._year;
&& d1._month == d2._month
&& d1._day == d2._day;
}
Date& operator=(const Date& d)
{
if(this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
bool operator<(const Date& d) {
if (_year < d._year)
return true;
else if (_year == d._year&&_month < d._month)
return true;
else if (_year == d._year&&_month == d._month&&_day < d._day)
return true;
return false;
}
bool operator<=(const Date& d) {
return *this < d || *this == d;//复用
}
bool operator>(const Date& d) {
return !(*this <= d);
}
bool operator>=(const Date& d) {
return !(*this < d);
}
bool operator!=(const Date& d) {
return !(*this == d);
}
Date operator+(int day) {
//Date ret(*this);//用d1拷贝构造一个ret
//ret._day += day;
//while (ret._day > GetMonthDay(ret._year, ret._month)) {
// //如果日期的天不合法,就需要往月进位
// ret._day -= GetMonthDay(ret._year, ret._month);
// ret._month++;
// if (ret._month == 13) {
// ret._year++;
// ret._month = 1;
// }
//}
//return ret;
//复用+=实现+
Date ret(*this);
ret += day;
return ret;
}
Date& operator+=(int day) {
if (day < 0)
return *this -= -day;
_day += day;
while (_day > GetMonthDay(_year, _month)) {
_day -= GetMonthDay(_year, _month);
++_month;
if (_month == 13) {
++_year;
_month = 1;
}
}
return *this;
}
Date& operator-=(int day) {
if (day < 0)
return *this += -day;
_day -= day;
while (_day <= 0) {//day不合法需要继续处理
--_month;
if (_month == 0) {
--_year;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
Date operator-(int day) {
/*Date ret = *this;
ret._day -= day;
while (ret._day <= 0) {
--ret._month;
if (ret._month == 0) {
--ret._year;
ret._month = 12;
}
ret._day += GetMonthDay(ret._year, ret._month);
}
return ret;*/
Date ret = *this;
ret -= day;
return ret;
}
//++d1
Date& operator++() {
*this += 1;
return *this;//返回加之后的值
}
//d1++
Date operator++(int) {//(int)为了构成函数重载
Date tmp(*this);
*this += 1;
return tmp;//返回加之前的值
}
//--d1
Date& operator--() {
*this -= 1;
return *this;
}
//d1--
Date operator--(int) {
Date tmp(*this);
*this -= 1;
return tmp;
}
int operator-(const Date& d) {
int flag = 1;
Date max = *this;//拷贝构造
Date min = d;
if (*this < d) {
max = d;//operator=赋值
min = *this;
int flag = -1;
}
int n = 0;
while (min != max) {
++min;
++n;
}
return n*flag;
}
int GetMonthDay(int year, int month)
{
static int monthDays[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
//是2月且是闰年,返回29
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
return 29;
}
return monthDays[month];
}
- const修饰类的成员函数:const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
(1) const对象不可以调用非const成员函数
(2) 非const对象可以调用const成员函数
(3) const成员函数内不可以调用其它的非const成员函数
(4) 非const成员函数内可以调用其它的const成员函数 - 取地址及const取地址操作符重载:一般不用重新定义 ,编译器默认会生成。
Date* operator&()
{
return this ;
}
const Date* operator&()const
{
return this ;
}
- 初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括 号中的初始值或表达式。
(1) 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
(2) 类中包含以下成员,必须放在初始化列表位置进行初始化:引用成员变量, const成员变量, 自定义类型成员。
(3) 尽量使用初始化列表初始化。
(4) 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
- explicit关键字:用explicit修饰构造函数,将会禁止单参数构造函数的隐式转换。
- static成员:声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。
(1) 静态成员为所有类对象所共享,不属于某个具体的实例。
(2) 静态成员变量必须在类外定义,定义时不添加static关键字。
(3) 类静态成员即可用类名::静态成员或者对象.静态成员来访问。
(4) 静态成员函数没有隐藏的this指针,不能访问任何非静态成员。
(5) 静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值。
注:在c++11中,非静态成员变量,可以在成员声明时给缺省值。 - 友元:友元分为友元函数和友元类。
1、友元函数
(1) 友元函数可以直接访问类的私有成员和保护成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
(2) 友元函数不能用const修饰。
(3) 一个函数可以是多个类的友元函数。
class Date
{
friend ostream& operator<<(ostream& _cout, const Date& d);
friend istream& operator>>(istream& _cin, Date& d);
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& d) {
_cout<<d._year<<"-"<<d._month<<"-"<<d._day;
return _cout; }
istream& operator>>(istream& _cin, Date& d) {
_cin>>d._year;
_cin>>d._month;
_cin>>d._day;
return _cin; }
2、友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
(1) 友元关系是单向的。
(2) 友元关系不能传递。
class Date; // 前置声明
class Time
{
friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成
员变量
public:
Time(int hour, int minute, int second)
: _hour(hour)
, _minute(minute)
, _second(second)
{}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
void SetTimeOfDate(int hour, int minute, int second)
{
// 直接访问时间类私有的成员变量
_t._hour = hour;
_t._minute = minute;
_t.second = second;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
- 内部类:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。
(1) 内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去调用内部类。
(2) 内部类就是外部类的友元类,但是外部类不是内部类的友元。
(3) sizeof(外部类)=外部类
class A {
private:
static int k;
int h;
public:
class B
{
public:
void f(const A& a)
{
cout << k << endl;
cout << a.h << endl;
}
};
};