C++初级-类和对象(2)
1.拷贝构造函数
(1)是特殊的成员函数
(2)是构造函数的一个重载形式
(3)参数只有一个并且必须使用引用传参,使用传值方式会引发无限的递归调用
(4)若未显示定义,系统则生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储,按字节序完成拷贝,称为浅拷贝或者值拷贝
#pragma once
#include<iostream>
using namespace std;
class Date
{
private:
int _year;
int _month;
int _day;
public:
Date(int year, int month, int day) :_year(year), _month(month), _day(day)
{}
Date(const Date& date) :_year(date._year), _month(date._month), _day(date._day)
{}
void display()
{
cout << _year << " " << _month << " " << _day << endl;
}
};
#include"date.h"
#include<iostream>
using namespace std;
int main()
{
Date d1(2009, 8, 13);
Date d2(d1);
d1.display();
d2.display();
return 0;
}
2.赋值运算符重载
函数原型:返回值类型 operator操作符(参数列表)
(1)不可以连接其他符号来创建新的操作符:比如operator@
(2)操作符必须是有一个类类型或者枚举类型的操作数
(3)用于内置类型的操作符(比如+),其含义不可以改变
(4)作为类成员的重载函数时,其形参的操作数目比实际的操作数目少1,因为有一个默认的形参this,限定为第一个形参
(5).*、::、sizeof、?:、.这5个运算符不能重载
运算符重载:
#pragma once
#include<iostream>
using namespace std;
class Date
{
private:
int _year;
int _month;
int _day;
public:
Date(int year, int month, int day) :_year(year), _month(month), _day(day)
{}
bool operator==(const Date& date)
{
return (_year == date._year) && (_month == date._month) && (_day == date._day);
}
};
#include"date.h"
#include<iostream>
using namespace std;
int main()
{
Date d1(2009, 8, 13);
Date d2(2009,8,13);
cout << (d1 == d2) << endl;
return 0;
}
赋值运算符重载(默认成员函数):
(1)检测自己为自己赋值
(2)返回*this
(3)未显示定义,编译器会自动生成,完成浅拷贝(值拷贝)
#pragma once
#include<iostream>
using namespace std;
class Date
{
private:
int _year;
int _month;
int _day;
public:
//构造函数
Date(int year, int month, int day) :_year(year), _month(month), _day(day)
{}
//运算符重载
bool operator==(const Date& date)
{
return (_year == date._year) && (_month == date._month) && (_day == date._day);
}
//赋值运算符重载
Date& operator=(const Date& date)
{
if (this != &date)
{
_year = date._year;
_month = date._month;
_day = date._day;
}
return *this;
}
};
#include"date.h"
#include<iostream>
using namespace std;
int main()
{
Date d1(0,0,0);//构造函数
Date d2(2009,10,8);//构造函数
d1 = d2;//赋值运算符重载
cout << (d1==d2) << endl;//运算符重载
return 0;
}
3.const 成员函数
将const修饰的类成员函数称为const成员函数,const成员函数实际修饰的是成员函数隐含的const指针,表明在该成员函数中不能对类的任何成员进行修改
例如:
void display()const
等价于:
void display(const Date& this)
const不能调用非const,非const可以调用const:
(1)const对象不能调用非const成员函数
(2)非const对象可以调用const成员函数
(3)const成员函数不能调用非const成员函数
(4)非const成员函数可以调用const成员函数
4.取地址
#pragma once
#include<iostream>
using namespace std;
class Date
{
private:
int _year;
int _month;
int _day;
public:
Date(int year=0, int month=0, int day=0) :_year(year), _month(month), _day(day)
{
cout << "Date()" << endl;
}
Date* operator&()
{
return this;
}
const Date* operator&()const
{
return this;
}
};
#include"date.h"
#include<iostream>
using namespace std;
int main()
{
Date d1;
const Date d2;
cout << &d1 << endl;
cout << &d2 << endl;
return 0;
}
默认成员函数应用实例:
#pragma once
#include<iostream>
using namespace std;
class Date
{
private:
int _y;
int _m;
int _d;
public:
//构造函数
Date(int y, int m, int d) :_y(y), _m(m), _d(d)
{}
//拷贝构造函数
Date(const Date& date) :_y(date._y), _m(date._m), _d(date._d)
{}
//析构函数
~Date()
{}
//赋值运算符重载
Date& operator=(const Date& date)
{
if (this != &date)
{
_y = date._y;
_m = date._m;
_d = date._d;
}
}
//运算符重载
bool operator==(const Date& date) const
{
return (_y == date._y) && (_m == date._m) && (_d == date._d);
}
bool operator!=(const Date& date)const
{
return (_y != date._y) || (_m != date._m) || (_d != date._d);
}
bool operator>=(const Date& date)const
{
if (_y >= date._y)
{
if (_y == date._y)
{
if (_m >= date._m)
{
if (_m == date._m)
{
if (_d >= date._d)
{
return true;
}
else
{
return false;
}
}
else
{
return true;
}
}
else
{
return false;
}
}
else
{
return true;
}
}
else
{
return false;
}
}
bool operator<=(const Date& date)
{
return (!(*this >= date)) || (*this == date);
}
//前置++
Date& operator++()
{
int month_pool[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
//判断闰年
if (((_y % 4 == 0) && (_y % 100 != 0) || (_y % 400 == 0)))
{
month_pool[1]=29;
}
//在本月
if (_d < month_pool[_m - 1])
{
_d+=1;
}
//在下个月1日
else if (_m < 12)
{
_m += 1;
_d = 1;
}
//在下一年1月1日
else
{
_y += 1;
_m = 1;
_d = 1;
}
return *this;
}
//后置++
Date operator++(int)
{
Date old(*this);//拷贝构造
++old;
return old;
}
Date operator+(int days)
{
Date old(*this);
for (int i = 0; i < days; i++)
{
++old;
}
return old;
}
//前置--
Date& operator--()
{
int month_pool[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
//判断闰年
if (((_y % 4 == 0) && (_y % 100 != 0)) || (_y % 400 == 0))
{
month_pool[1] = 29;
}
//在本月
if (_d > 1)
{
_d -= 1;
}
//在上个月
else if (_m > 1)
{
_m -= 1;
_d = month_pool[_m - 1];
}
//在上一年
else
{
_y -= 1;
_m = 12;
_d = month_pool[_m - 1];
}
return *this;
}
//后置--
Date operator--(int)
{
Date old(*this);
--old;
return old;
}
Date operator-(int days)
{
Date old(*this);
for (int i = 0; i < days; i++)
{
--old;
}
return old;
}
void display()const
{
cout << _y << " " << _m << " " << _d << endl;
}
};
#include"date.h"
#include<iostream>
using namespace std;
int main()
{
Date d1(1999, 1, 2);
d1.display();
cout << endl;
Date d2(2019, 1, 2);
d2.display();
cout << endl;
Date d3(d2);
d3.display();
cout << endl;
Date d4 = d1;
d4.display();
cout << endl;
cout << (d1 == d4) << endl;
cout << (d1 == d2) << endl;
cout << endl;
cout << (d1 != d2) << endl;
cout << (d2 != d3) << endl;
cout << endl;
cout << (d1 <= d2) << endl;
cout << (d2 <= d3) << endl;
cout << (d3 <= d1) << endl;
cout << endl;
cout << (d2 >= d1) << endl;
cout << (d3 >= d2) << endl;
cout << (d4 >= d3) << endl;
cout << endl;
Date d5(2019, 12, 29);
Date d6(2019, 12, 31);
Date d7(2019, 2, 28);
Date d8(2020, 2, 28);
Date d9(2020, 2, 29);
Date d0(2000, 2, 28);
cout << endl;
(++d5).display();
(++d6).display();
(++d7).display();
(++d8).display();
(++d9).display();
(++d0).display();
cout << endl;
(d5++).display();
(d6++).display();
(d7++).display();
(d8++).display();
(d9++).display();
(d0++).display();
cout << endl;
Date d(2018, 2, 9);
d.display();
(d + 796).display();
cout << endl;
Date d10(2019, 1, 2);
Date d11(2019, 1, 1);
Date d12(2019, 3, 1);
Date d13(2020, 3, 1);
cout << endl;
(--d10).display();
(--d11).display();
(--d12).display();
(--d13).display();
cout << endl;
(d10--).display();
(d11--).display();
(d12--).display();
(d13--).display();
cout << endl;
Date dd(2020, 4, 15);
dd.display();
(dd - 796).display();
return 0;
}
5.初始化列表
在创建对象时,编译器调用构造函数为对象的各个成员变量赋一个合适的初值。称为赋初值,而不是初始化。初始化只能初始化一次,而构造函数体内可进行多次赋值。
(1)初始化列表:以一个冒号开始,每个成员变量后面跟一个放在括号中的初始值和表达式,中间用逗号隔开
class Date
{
private:
int _year;
int _month;
int _day;
public:
//构造函数
Date(int year, int month, int day) :_year(year), _month(month), _day(day)
{}
(2)只能初始化一次
(3)引用成员变量,类类型成员变量,const成员变量(必须赋初值)必须在初始化列表内进行初始化
class A
{
private:
int _a;
public:
A(int a) :_a(a)
{}
};
class B
{
private:
A _a;
int& _ref;
const int _n;
public:
B(int a, int ref) :_a(a), _ref(ref), _n(10)
{}
};
(4)对于自定义类型成员变量,先使用初始化列表初始化
(5)初始化列表的初始化顺序由成员变量在类中的声明次序决定
6.explicit关键字
构造函数不仅可以构造与初始化对象,对于单个参数的构造函数,还具有类型转换的作用。用explicit修饰构造函数,将会禁止单参构造函数的隐式转换。
7.static成员
声明为static的类成员称为类的静态成员。
- 静态成员变量:static修饰的成员变量
- 静态成员函数:static修饰的成员函数
注:静态成员变量一定要在类外初始化 - 静态成员为所有类对象共享,不属于某个具体实例
- 静态成员变量必须在类外定义,定义不加static关键字
- 类静态成员可用"类名::静态成员"或"对象.静态成员"访问
- 静态成员函数没有隐藏的this指针,不能访问非静态成员
- 静态成员函数不能调用非静态成员函数;非静态成员函数可以调用静态成员函数
#include<iostream>
using namespace std;
class A
{
public:
A()
{
++_count;
}
A(const A& a)
{
++_count;
}
static int getCount()
{
return _count;
}
private:
static int _count;
};
int A::_count = 0;
int main()
{
cout<<A::getCount()<<endl;
A a1,a2;
A a3(a1);
cout<<A::getCount()<<endl;
return 0;
}
8.友元:会增加耦合度,但是破坏了封装
(1)友元函数
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
- 友元函数可以访问类的私有成员,但不是类的成员函数
- 友元函数不能用const修饰
- 友元函数可以在类的任何地方声明,不受访问限定符限制
- 一个函数可以是多个类的友元函数
class Date
{
friend istream& operator>>(istream& _cin, const Date& date);
friend ostream& operator<<(ostream& _cout, const Date& date);
private:
int _y;
int _m;
int _d;
public:
Date(int y=0, int m=0, int d=0) :_y(y), _m(m), _d(d)
{}
};
istream& operator>>(istream& _cin, const Date& date)//输入
{
_cin >> date._y;
_cin >> date._m;
_cin >> date._d;
return _cin;
}
ostream& operator<<(ostream& _cout, const Date& date)//输出
{
_cout << date._y << " " << date._m << " " << date._y << endl;
return _cout;
}
int main()
{
Date d;
cin >> d;
cout << d << endl;
return 0;
}
(2)友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员
- 友元关系是单向的,没有交换性
- 友元关系不能传递
class Date;
class Time
{
friend class Date;
private:
int _h;
int _mi;
int _s;
public:
Time(int h = 0, int mi = 0, int s = 0):_h(h),_mi(mi),_s(s)
{}
};
class Date
{
private:
int _y;
int _m;
int _d;
Time _t;
public:
Date(int y = 2020, int m = 4, int d = 16) :_y(y), _m(m), _d(d)
{}
void setTime(int h, int mi, int s)
{
_t._h = h;
_t._mi = mi;
_t._s = s;
}
void display()
{
cout << _y << " " << _m << " " << _d << " " << _t._h << " " <<_t ._mi << " " << _t._s << endl;
}
};
int main()
{
Date d;
d.setTime(22,17,45);
d.display();
return 0;
}
9.内部类:定义在一个类内部的类
(1)内部类是一个独立的类,不属于外部类
(2)外部类对象不能调用内部类
(3)内部类为外部类的友元类,可通过外部类对象参数访问外部类成员
(4)内部类可以直接访问外部类中的static,枚举成员,不需要外部类的对象/类名
(5)sizeof(外部类)=外部类,和内部类没有关系
class A
{
private:
static int a;
int _b;
public:
A(int b=1):_b(b){}
class B
{
public:
void display(const A& obj)
{
cout << a << endl;
cout << obj._b << endl;
}
};
};
int A::a = 0;
int main()
{
A a;
A::B ab;
ab.display(a);
return 0;
}
10.封装:通过类,将对象的所有东西打包在一起,通过访问限定符选择性的将其部分功能开放出来与其他对象进行交互,而对于对象内部的一些实现细节,外部用户不需要知道
面向对象三大特性:封装,继承,多态