作者: sleepygod
日期: 4-16
也许 不负光阴就是最好的努力 而努力就是最好的自己
文章目录
c++类和对象
1.类和对象基础概念
1.类的引入
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。比如: 之前在数据结构初阶中,用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现, 会发现struct中也可以定义函数。在C++中更喜欢用class来代替。
下面构建一个日期类
class Date
{
public:
Date(int year, int month, int day);
Date(const Date& d);
private:
int _year;
int _month;
int _day;
};
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
2.类的定义
class className
{
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。
类的两种声明方式:
第一种声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内 联函数处理。
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
第二种类声明与成员函数分离,注意:成员函数名前需要加类名::
class Date
{
public:
Date(int year, int month, int day);
Date(const Date& d);
private:
int _year;
int _month;
int _day;
};
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
一般情况下,更倾向于第二种写法.第二种写法当后期维护查看代码时,会比较轻松.
3.类的封装
1.访问限定符
有public, protected,private三类
public修饰的成员在类外可以直接被访问.
protected,private修饰的成员在类外不可以直接被访问.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BzB5wXsr-1681652973895)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20221028222032105.png)]
class的默认访问权限为private,struct为public.
面向对象的三大特征:封装,继承,多态(当然不止这几个特征,因为这三个最出名).
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来 和对象进行交互.
4.类的作用域与实例化
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域.
class Date
{
public:
Date(int year, int month, int day);
Date(const Date& d);
private:
int _year;
int _month;
int _day;
};
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
用类类型创建对象的过程,称为类的实例化
如上面 Date A;即为实例化.
5.类的大小
类的存储方式:只保存成员变量,成员函数存放在公共的代码段.
所以类的大小,实际就是该类的中"成员变量"之和,与结构体的大小计算相同,需要注意默认对齐数.
当类为空类时,编译器会给空类分配一个字节来唯一标识这个类.
6.this指针
this指针类型: 类类型*const 即在函数中不能给this赋值,只能在成员函数中使用.
this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给 this形参。所以对象中不存储this指针.
this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传 递,不需要用户传递
class Date
{
public:
Date(int year, int month, int day);
Date(const Date& d);
void Print()
{
cout << _year;
}
private:
int _year;
int _month;
int _day;
};
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
int main()
{
Date a(2001, 10, 20);
a.Print();
return 0;
}
对于上述代码解释 当调用成员函数Print()时,实际上是Print(Date *this);所以输出语句是 cout<<this->_year
;
但是在一般情况下都不写,来简化代码. 注:this指针可以指向空.且this指针存储在栈区
2.类和对象重要函数
6个默认成员函数
1.构造函数
example:
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证 每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。即替代了我们对对象初始化的过程.
特性
构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任 务并不是开空间创建对象,而是初始化对象。
特征如下:
- 函数名与类名相同。
- 无返回值。
- 对象实例化时编译器自动调用对应的构造函数。
- 构造函数可以重载。
- 如果类中没有显示定义构造函数,则c++编译器会自动生成一个无参的默认构造.此构造函数对内置类型不做处理,对自定义类型才会做处理
2.析构函数
日期类中不需要写析构函数,所以此处不在展示样例
与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由 编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
特性如下:
- 析构函数名是在类名前加上字符 ~。
- 无参数无返回值类型。
- 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构 函数不能重载
- 对象生命周期结束时,C++编译系统系统自动调用析构函数。
- 如果类中没有显示定义析构函数,则c++编译器会自动生成一个析构函数.
3.拷贝构造函数
example:
Date::Date(const Date& d)
{
if (&d == nullptr)
{
*this=Date();
}
else
{
_year = d._year;
_month = d._month;
_day = d._day;
}
}
拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存 在的类类型对象创建新对象时由编译器自动调用。
特性如下:
-
拷贝构造函数是构造函数的一个重载形式。
-
拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。
-
若未显式定义,编译器会生成默认的拷贝构造函数。此拷贝构造是浅拷贝,对于属性是内置类型的类适用,对于自定义类型,则可能会产生一些错误,在后续过程中会讲解到.
4.赋值运算符重载
example:
Date& Date::operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其 返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。其中.* :: sizeof ?: . 五个运算符不能被重载. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝和默认拷贝构造类似.
5.普通对象取地址重载
example
Date* Date::operator&()
{
return nullptr;
}
无法得到对象的地址
使用不多,就不做讲解
6.const对象取地址重载
example:
const Date* Date::operator&()const
{
return nullptr;
}
无法得到对象的地址
使用不多,就不做讲解
3.类和对象升级讲解
1.构造函数的其他形式
构造函数的函数体通过赋值的方式来给数据成员指定初始值。也就是说构造函的函数体是采用先定义后赋值的方式来做。但是这种方式会产生一些问题,比如const类型数据,引用类型数据,必须在定义时进行赋值.
所以,我们没有办法对于这些类型数据赋值吗?当然不是,现在将要提出初始化列表
初始化列表使用初始化的方式来为数据成员指定初始值。也就是说成员初始化列表是在数据成员定义的同时赋初值。
Date::Date(int year, int month, int day)
:_year(year)
,_month(month)
,_day(day)
{}
这是就可以解决const,引用类型的数据,自定义类型成员(且该类没有默认构造函数时).并且尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量, 一定会先使用初始化列表初始化。
同时员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后 次序无关
Date::Date(int year, int month, int day)
:_day(day)
,_month(month)
, _year(_day)
{}
2.explicit关键字
造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值 的构造函数,还具有类型转换的作用。
用explicit修饰构造函数,将会禁止构造函数的隐式转换
3.static成员
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用 static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化
静态成员使用
#pragma once
#include<iostream>
using namespace std;
class MyClass
{
public:
MyClass();
static int get_a() { return _a; }
private:
static int _a;
};
int MyClass::_a = 0;
MyClass::MyClass()
{
_a++;
}
int main()
{
MyClass a[50];
cout<< MyClass::get_a() << endl;
return 0;
}
结果:50
4.友元函数
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在 类的内部声明,声明时需要加friend关键字。并且不能用const修饰,不受访问限定符控制.使用方式如下:
lass Date
{
friend ostream& operator<<(ostream& _cout, const Date& d);
friend istream& operator>>(istream& _cin, Date& d);
friend void swap(Date& d1, Date& d2);
...
}
4.日期类实现
//h
#pragma once
#include<iostream>
#include<string>
#include<vector>
using namespace std;
class Date
{
friend ostream& operator<<(ostream& _cout, const Date& d);
friend istream& operator>>(istream& _cin, Date& d);
friend void swap(Date& d1, Date& d2);
public:
int getMonthDay();
Date(int year=2001, int month=1, int day=1);
Date(const Date& d);
Date& operator=(const Date& d);
Date& operator-=(int day);
Date& operator+=(int day);
Date operator-(int day)const;
Date operator+(int day)const;
Date& operator++();
Date operator++(int);
Date& operator--();
Date operator--(int);
int operator-(const Date& d)const;
bool operator==(const Date& d)const;
bool operator!=(const Date& d)const;
bool operator>(const Date& d)const;
bool operator>=(const Date& d)const;
bool operator<(const Date& d)const;
bool operator<=(const Date& d)const;
void display();
private:
int _year;
int _month;
int _day;
};
void swap(Date& d1, Date& d2);
ostream& operator<<(ostream& _cout, const Date& d);
istream& operator>>(istream& _cin, Date& d);
//cpp
#include"Date.h"
int Date::getMonthDay()
{
vector <int> month = { 31,28,31,30,31,30,31,31,30,31,30,31 };
/*if (_year % 400 == 0 || (_year % 4 == 0 && _year % 100 != 0))
{
month[1] = 29;
}*/
return month[_month - 1];
}
Date::Date(int year, int month, int day)
:_year(year)
,_month(month)
,_day(day)
{}
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
Date& Date::operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
Date& Date::operator+=(int day)
{
if (day < 0)
{
*this -= -day;
return *this;
}
_day += day;
while (_day > getMonthDay())
{
_day -= getMonthDay();
_month++;
if (_month > 12)
{
_month = 1;
_year++;
}
}
return *this;
}
Date& Date::operator-=(int day)
{
if (day < 0)
{
*this += -day;
return *this;
}
_day -= day;
while (_day <1)
{
_month--;
if (_month < 1)
{
_month = 12;
_year--;
}
_day += getMonthDay();
}
return *this;
}
Date Date::operator-(int day)const
{
Date tmp(*this);
return tmp -= day;
}
Date Date::operator+(int day)const
{
Date tmp(*this);
return tmp += day;
}
Date& Date::operator++()
{
return *this += 1;
}
Date Date::operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp ;
}
Date& Date::operator--()
{
return *this -= 1;
}
Date Date::operator--(int)
{
Date tmp(*this);
*this -= 1;
return tmp;
}
int Date::operator-(const Date& d)const
{
Date max = *this;
Date min = d;
int flag = 1;
if(max < min)
{
swap(max, min);
flag = -1;
}
int day = 0;
while ((min +day)!= max)
{
day++;
}
return day * flag;
}
bool Date::operator==(const Date& d)const
{
if (_year == d._year && _month == d._month && _day == d._day)
{
return true;
}
return false;
}
bool Date::operator!=(const Date& d)const
{
return !(*this == d);
}
bool Date::operator>(const Date& d)const
{
if (_year > d._year)
{
return true;
}
else if (_year == d._year)
{
if (_month > d._month)
{
return true;
}
else if (_month == d._month)
{
if (_day > d._day)
{
return true;
}
}
}
return false;
}
bool Date::operator>=(const Date& d)const
{
return (*this==d) ||(*this > d) ;
}
bool Date::operator<(const Date& d)const
{
return !(*this >= d);
}
bool Date::operator<=(const Date& d)const
{
return !(*this > d);
}
void Date::display()
{
cout<<_year<<"-"<< _month << "-" << _day << endl;
}
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;
}
void swap(Date& d1, Date& d2)
{
std::swap(d1._year, d2._year);
std::swap(d1._month, d2._month);
std::swap(d1._day, d2._day);
}
//cpp
#include"Date.h"
int main()
{
Date d1(2022);
(d1 + 400).display();
Date d2(d1);
Date d3;
d3 = d1;
d1 += 400;
cout << d1 << d2 << d3<<endl;
d1.display();
d2.display();
d3.display();
cout << (d1 < d2);
cout << (d1 - d2)<<endl;
cout << (d2 - d1);
return 0;
}