学习C++,首先要了解的便是它和C语言的区别所在,很多的新手可能和我一般,学校是先开设了C语言,之后才开设的C++课程,因此学习C++之前,我们要明白:
两种定义类1.struct 类名 { }
2. class 类名 {}
,这里我们重点介绍的是后面一种。
class Student
{
public: //公有的成员在类外可见
//成员函数
void display()
{}
void setId(int id)
{
_id = id;
}
void setNumber(int number)
{
_number = number;
}
private: //私有的成员在类外不可见
//成员变量, 属性
int _number;
int _id;
protected: // 保护的成员在类外不可见
char name[10];
char gender[10];
char major[10];
};
【面试题】C++中的struct和class的区别是什么呢?
C++需要兼容C语言,所以C++中的struct可以当成结构体去进行使用,也可以用来定义类;它和class定义类是一样得,区别在于struct的成员默认访问方式是public,class的成员默认访问方式是private。
上面我们所看到的public和protected,private都是类的访问限定符
对于访问限定符的说明:
封装:用类将成员和函数将合在一起,使得对象更加的完善,通过访问权限选择性的将接口提供给外部用户来进行使用,实质上是一种管理。
如果在类体外定义成员的话,需要使用::
作用域解析符指明成员属于那个类域
class B
{
private:
int _a;
void fun() {
cout << "class B fun()" << endl;
}
void fun2();
};
void B::fun2() {//类域之中的fun2的定义
cout << "B::fun2()" << endl;
}
类的实例化:
类的大小也遵循内存对齐的规则:并且在类中嵌套类的时候,如果此类没有创建变量,不计算其内存大小
class G
{
char _c; //1
double _d; // 16
int _a; // 20
char _c2; //21
//24
//嵌套类本身遵循内存对齐的原则,计算大小: H: 24
class H
{
double _d; //8
char _c; //9
int _a; //16
char _c1; //17
//24
};
H _h;//未创建变量时,不计算内存大小
};
this指针:
this解引用:
如果一个类中什么成员都没有,简称为空类。空类中并不是什么都没有,任何一个类在外面不写的情况下,都会自动生成下面的6个默认成员函数。
构造函数是特殊的成员函数,需要注意的是,构造函数虽然名为构造,但其主要任务并不是开空间创建对象,而是初始化对象
编译器默认生成的构造函数
Date()
{
}
显示定义的无参构造
Date()
{
_year = y;
_month = m;
_day = d;
}
全缺省的构造函数
Date(int y = 2020, int m = 5, int d = 20)
{
_year = y;
_month = m;
_day = d;
}
重载构造函数
Date(float f)
{
}
explicit关键字
构造函数对于单个参数的构造函数还具有类型转换的作用(调用构造创建一个匿名对象,通过匿名对象来给所需要创建的对象进行赋值或拷贝构造),而为了避免这种隐式转换,可以使用explicit
关键字来修饰构造函数,将会禁止单参构造函数的隐式转换。
构造函数初始化列表
class Time {
public:
Time(int a = 1)
:_a(a)
{
cout << "Time(int)" << endl;
}
private://这里是成员变量声明的地方,而引用和const类型变量,这两者定义时必须初始化
int _a;
};
因为类的一些资源并不在类中,因此在对象生命周期结束的时候需要对资源进行清理和释放(时清理资源不是销毁对象),则会自动调用析构函数,完成资源清理的工作。
~
,没有参数也没有返回值class A {
public:
~A()
{
cout << "~A()" << endl;
}
int _a;
};
拷贝构造是构造函数的一个重载形式,它是用一个已经存在的对象去创建一个新的对象,创建的新对象和当前所存在的对象内容完全相同;
class Date {
public:
//构造函数
Date(int y = 1, int m = 1, int d = 1) {
_year = y;
_month = m;
_day = d;
}
//拷贝构造函数
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
void Test() {
Date d;
Date d2(2020, 4, 1);
Date& rd = d;
Date copy1(d);
Date copy2(Date(2020, 5, 20));//优化,直接调用构造函数创建copy2
//不优化:调用构造创建匿名对象,+ 拷贝构造
}
若没有显式定义拷贝构造函数的话,系统会默认生成拷贝构造函数,但智慧按照内存存储的字节序完成浅拷贝,拷贝对象模型中的内容,不会拷贝资源,因此如果需要拷贝资源一定要显式定义拷贝构造函数。
拷贝构造函数的参数只有一个(一般用const修饰),必须使用引用传参,如果使用传值方式的话会引发无穷次的递归调用。
运算符重载是具有特殊函数名的函数,也具备其返回值的类型,函数名,参数列表,其返回值类型和参数列表与普通的函数类似,其作用旨在增强代码的可读性,定义和使用与普通函数一致。
class Date {
public:
bool IsEqual(const Date& d) {
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
bool operator==(const Date& d) {//底层接口 bool operator==(Date* const this, const Date& d)
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
private:
int _year;
int _month;
int _day;
};
//输出运算符重载函数
ostream& operator<<(ostream& _cout, const Date& date) {
_cout << date._year << " " << date._month << " " << date._day << endl;
return _cout;
}
.* ,::,sizeof,?:, .
以上5个匀速那副不能重载,笔试选择题中较为热门赋值运算符重载:
class Date
{
public:
Date(int y = 1, int m = 1, int d = 1)
{
_year = y;
_month = m;
_day = d;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
cout << "Date(const Date& d)" << endl;
}
//优化 ,避免自己给自己赋值
Date& operator=(const Date& d2)
{
//判断是否给自己赋值
if (this != &d2)
{
_year = d2._year;
_month = d2._month;
_day = d2._day;
}
cout << "operator=(const Date& d2)" << endl;
//返回当前调用此函数的对象本身
return *this;
}
//private:
int _year;
int _month;
int _day;
};
void Test() {
Date d(2020, 5, 22);
Date d2(2019, 1, 1);
//如果对象都存在,调用赋值运算符重载函数,如果左边对象不存在,则调用拷贝构造
d2 = d;
d2.operator==(d);// 同上等价
d2 = d2;
Date d3(2018, 10, 1);
//连续赋值:从右向左赋值
d = d2 = decltype;
d.operator=(d2.operator=(d3));//同上面等价
Date d4 = d3;//因为d4不存在,则调用拷贝构造,用d3创建d4对象
}
d=d2
,修改已经存在的对象内容,不是去创建新的对象将const修饰的类成员函数称之为const函数,它实际修饰的是该成员函数的隐式this指针,表明在该成员函数中不能够对类的任何成员进行修改。
const函数和非const函数
void printD() // 等价于 printD(Date* const this)
{
cout << _year << " " << _month << " " << _day << endl;
//可以修改内容
this->_year = 100;
//可以调用const成员函数
fun();
}
void printD() const //等价于 printD(const Date* const this)
{
cout << _year << " " << _month << " " << _day << endl;
//不能修改内容 this->_year = 100;这是错误的
//不能调用非const成员函数,读写的权限不能被放大 fun()
//不能进行自加操作 ++*this;
}
void fun()const
{
}
class Date
{
public :
Date* operator&()
{
return this ;
}
const Date* operator&()const
{
return this ;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};
这两个运算符一般不需要重载使用编译器默认生成的即可,如果想要让别人获取到指定的内容的话,才需要进行重载。
//int cnt = 0; //定义全局变量的话,安全性较低,容易篡改
class Date
{
public:
Date(int year = 2020, int month = 12, int day = 20)
:_year(year)
, _month(month)
, _day(day)
{
++cnt;
cout << "Date(int ,int ,int)" << endl;
}
Date(const Date& d)
:_year(d._year)
, _month(d._month)
, _day(d._day)
{
++cnt;
cout << "Date(const Date&)" << endl;
}
//静态成员函数:函数内部没有this指针
static int getCount()
{
}
void Display()
{
cout << _year << "-" << _month << "-" << _day << endl;
getCount();
cout << cnt << endl;
}
private:
int _year = 1;
int _month = 1;
int _day = 1;
public:
static int cnt;
};
//静态成员必须在类外初始化
int Date::cnt = 0;
Date fun(Date d) //拷贝构造
{
cout << &d.cnt << endl;
return d;
}
void Test()
{
Date d;//构造
Date d2 = fun(d);//拷贝函数 fun:进行优化,只有两次拷贝构造,传参创建d2
//静态成员变量/静态成员函数访问方式:
// 1. 对象访问
cout << d.getCount() << endl;
cout << d2.getCount() << endl;
cout << &d.cnt << endl;
cout << &d2.cnt << endl;
// 2. 类名 + 作用域限定符
cout << &Date::cnt << endl;
cout << Date::cnt << endl;
cout << Date::getCount() << endl;
//普通成员只能通过对象访问,不能通过类名访问
d.Display();
//Date::Display(); //不支持
}
对于C++11的初始化方式,相对于给一个缺省值
private:
int _year = 1;
int _month = 1;
int _day = 1;
它将是初始化时候的最后一个候选,先优先前面的进行选择
如果有缺省的构造函数,那么就先优先使用缺省构造函数的数据
class Date
{
public:
friend ostream& operator<<(ostream& outputS, Date& d);
friend istream& operator >> (istream& inputS, Date& d);
friend class B;
}
class B
{
public:
//disPlay, fun, fun1都为Date类的的友元函数
void disPlay(const Date& d)
{
cout << d._year << d._month << d._day << endl;
}
void fun(const Date& d)
{
cout << d._year << d._month << d._day << endl;
}
void fun1(const Date& d)
{
cout << d._year << d._month << d._day << endl;
}
};
输出流和输入流
ostream& operator<<(ostream& outputS, Date& d)
{
outputS << d._year << "-" << d._month << "-" << d._day << endl;
return outputS;
}
istream& operator >> (istream& inputS, Date& d)
{
inputS >> d._year >> d._month >> d._day;
return inputS;
}
enum Color
{
BLACK,
WHITE
};
class C
{
public:
class D
{
public:
void fun(const C& c)
{
//可以通过外部类对象访问外部类的私有成员
cout << c._color << endl;
cout << c._c << endl;
cout << c._sc << endl;
cout << C::_sc << endl;
//可以直接访问外部类的static成员
cout << _sc << endl;
}
private:
int _d;
};
private:
int _c;
static int _sc;
Color _color;
//内部类可以在类的任何地方定义
class E
{
private:
int _e;
};
};
#include
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)//构造函数
{
//在进行一个日期赋值前需要判断日期是否合法
if (year > 0 && month > 0 && month < 13
&& day > 0 && day <= getMonthDay(year, month))
{
_year = year;
_month = month;
_day = day;
}
else
{
cout << "日期不合法: " << year << "-" << month << "-" << day << endl;
cout << "重置为默认值: 2000-1-1" << endl;
_year = 2000;
_month = 1;
_day = 1;
}
}
int getMonthDay(int year, int month)//获得当前月份的天数
{
static int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int day = days[month];
//如果是2月且为闰年,+1
if (month == 2
&& (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)))
++day;
return day;
}
// a += b
Date& operator+=(int day) //加等操作
{
// a += -b --> a -= b
if (day < 0)
return *this -= -day;
//2020.5.1 + 20 --> 2020.5.21
//2020.5.21 + 20 --> 2020.5.41 --> 进位 --> -31 -->月份进位 --> 2020.6.10
//2020.12.6 + 90 --> 2020.12.96 --> 进位 --> -31 -->月份进位 --> 2020.13.65 -->年进位
// --> 2021.1.65--> --> 进位 --> -31 -->月份进位 --> 2021.2.34 --> --> 进位 --> -28 -->月份进位 --> 2021.3.6
_day += day;
//判断日期是否溢出,溢出需要进位
while (_day > getMonthDay(_year, _month))
{
//减去当月的天数,月份进位
_day -= getMonthDay(_year, _month);
++_month;
//判断月份是否溢出
if (_month == 13)
{
//进位到下一年的1月
_month = 1;
_year++;
}
}
return *this;
}
//前置++: ++d: 首先++,返回++之后的值
Date& operator++()
{
return *this += 1;
//return (*this).operator+=(1);
//return *this;
}
//后置++: d++: 本身++, 返回++之前的值
//前置++, 后置++都为单目运算符
//如果为成员函数,则本质上不需要显式传参,编译器会自动传入this指针
//int: 形参不是一个真正的参数,只是一个标记参数,编译器看到这样的定义,通过语法树搜索,可以解释为后置++
Date operator++(int)
{
//保存++之前的值
Date ret(*this);
//++
*this += 1;
//返回++之前的值
return ret;
}
Date& operator-=(int day)//减等操作
{
if (day < 0)
return *this += -day;
_day -= day;
//判断_day是否为负值或者0, 退位
//2020.5.24 - 30 --> 2020.5.-6 --> 月份退位 --> +30 --> 2020.4.24
while(_day <= 0)
{
//月份退位
--_month;
//月份是否为负值或者0
if (_month == 0)
{
_month = 12;
//年份退位
--_year;
}
_day += getMonthDay(_year, _month);
}
return *this;
}
Date& operator--()//前置减减操作
{
return *this -= 1;
}
Date operator--(int)//后置减减操作
{
Date ret = *this;
//Date ret(*this);
*this -= 1;
return ret;
}
// +, -运算符:不能修改操作数的内容
// c = a + b
Date operator+(int day)//加运算符
{
Date ret = *this;
ret += day;
return ret;
}
Date operator-(int day)//减运算符
{
Date ret = *this;
ret -= day;
return ret;
}
bool operator==(const Date& date)//比较相等
{
return _year == date._year
&& _month == date._month
&&_day == date._day;
}
bool operator>(const Date& date)//大于
{
if (_year > date._year)
return true;
else if (_year == date._year)
{
if (_month > date._month)
return true;
else if (_month == date._month)
{
if (_day > date._day)
return true;
}
}
return false;
}
bool operator>=(const Date& date)//大于等于
{
return (*this > date) || (*this == date);
}
bool operator<(const Date& date)
{
return !(*this >= date);
}
bool operator<=(const Date& date)
{
return !(*this > date);
}
bool operator!=(const Date& date)
{
return !(*this == date);
}
void printD() // 等价于 printD(Date* const this)
{
cout << _year << " " << _month << " " << _day << endl;
//可以修改内容
//this->_year = 100;
//可以调用const成员函数
fun();
}
//const成员函数中的const修饰的为第一个参数,即this指针
//const成员函数内部补能修改成员变量的值
void printD() const //等价于 printD(const Date* const this)
{
cout << _year << " " << _month << " " << _day << endl;
//不能修改内容
//this->_year = 100;
//不能调用非const成员函数,读写的权限不能被放大
//++*this;
}
void fun()const
{
}
int operator-(Date& date)//两个日期进行相减
{
Date d1(*this);
Date d2(date);
//d1 - d2
int num = 0;
if (d1 > d2)
{
while (d1 > d2)
{
--d1;
++num;
}
return num;
}
else
{
//d1 <= d2
while (d1 < d2)
{
++d1;
++num;
}
return -num;
}
}
/*Date operator-(Date& date)
{
}*/
//取地址运算符重载函数: operator&
//一般不需要显示定义,直接用默认即可
Date* operator&()
{
//return (Date*) 0x1234;
return this;
}
const Date* operator&() const
{
//return nullptr;
return this;
}
//private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& date)
{
_cout << date._year << " " << date._month << " " << date._day << endl;
return _cout;
}