一、const成员
将const修饰的"成员函数"称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Display()const
//编译器处理为 void Display(const Date* this)
{
cout << "Display()const 调用" << endl;
cout << _year << endl;
cout << _month << endl;
cout << _day << endl;
}
void Display()
{
cout << "Display() 调用" << endl;
cout << _year << endl;
cout << _month << endl;
cout << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2023, 3, 29);
d1.Display();
const Date d2(2023, 3, 29);
d2.Display();
return 0;
}
注意:const修饰是权限的缩小,非const成员函数可以调用const成员函数,const成员函数不能调用非const成员函数。
二、构造函数进阶
1.初始化列表
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号的初始值或表达式
C++采用初始化列表的方式对对象的属性进行初始化
1.每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
2.类中包含以下成员,必须放在初始化列表位置进行初始化:引用成员变量,const成员变量,自定义类型成员(且该类没有默认构造函数时)
初始化列表的初始化顺序取决于对象属性的声明顺序,与属性在初始化列表中的位置无关
不管是否使用初始化列表,对于自定义类型成员变量,一定要使用初始化列表初始化
2.explict关键字
构造函数不仅可以构造与初始化对象,对于单个参数或者出第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用
用explict修饰构造函数,将会禁止构造函数的隐式转换
C++中,一个参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造函数),承担了两个角色:1.是构造2.是个默认且隐含的类型转换操作符。然而某些情况下并不需要进行隐式类型转化,因此使用explict
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
class Date_2
{
public:
Date_2(int year,int month,int day,int a,int b)
:_year(year)
,_month(month)
,_day(day)
,aaa(a)
,bbb(b)
{
}
private:
int _year;
int _month;
int _day;
int& aaa;
const int bbb;
};
class Date_3
{
public:
/*explicit*/ Date_3(int year)
:_year(year)
{
}
Date_3& operator=(const Date_3& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date_3 d3(2023);
//如果构造函数有explict标识,则不能进行隐式类型转化,下面的赋值语句报错
d3 = 100;
//如果构造函数中没有explict标识,则不能进行隐式类型转化
//编译器首先将100进行拷贝一份,然后对d3进行赋值,
// 赋值的时候存在隐式类型转换(将100(int类型)转换成d3(Date_3)类型)
}
三、静态成员
声明为static的类成员称为类的静态成员
用static修饰的成员变量,称之为静态成员变量
用static修饰的成员函数,称之为静态成员函数。
静态成员变量一定要在类外进行初始化
静态成员变量:1.所有对象共享一份数据 2.在编译阶段分配内存 3.类内声明类外初始化
静态成员函数:1.所有对象共享同一个函数 2.静态成员函数只能访问静态成员变量
注意:1.静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
2.静态成员变量必须在类外定义,定义时不添加static关键字,类中知识声明
3.类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
4.静态成员函数没有隐藏的this指针,不能访问任何非静态成员
5.静态成员也是类的成员,受public、protected、private访问限定符的限制
class A
{
public:
A(int a = 0)
{
++count;
}
A(const A& a)
{
++count;
}
int GetCount()
{
return count;
}
static int GetCount_2()
{
//_num=1;//静态成员函数不可以访问成员变量,只能访问静态成员变量
return count;
}
private:
//声明为static的类成员称为类的静态成员;用static修饰的成员变量,称为静态成员变量
//static修饰的成员函数,称为静态成员函数
int _num;
static int count;//静态成员变量,属于所有对象,属于整个类
};
int A::count = 0;//静态成员变量一定要在类外进行实例化
int main()
{
/*A a1;
A a2(a1);*/
//如果count属于public作用域,那么可以通过以下方式在类中找到count
/*cout << a1.count << endl;
cout << A::count << endl;
A* p1 = nullptr;
cout << p1->count << endl;*/
//如果count属于private作用域,需要通过成员函数获得
A a3;
cout << a3.GetCount() << endl;
cout << A::GetCount_2() << endl;
return 0;
}
四、友元
1.友元函数
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字
注意:友元函数可访问类的私有成员变量,但不是类的成员函数
友元函数不能用const修饰
友元函数可以在类定义的任何地方声明,不受类访问限定符限制
一个函数可以是多个类的友元函数
友元函数的调用与普通函数的调用原理相同
需要提前声明要使用的类:必须将先定义的类的成员函数作为后定义的类的友元函数,如果调换顺序会出现语法错误
2.友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非共有成员
友元类,实际上就是把这个类定义为另一个类的友元类(即A类是B类的友元,A可以访问B中的私有成员变量以及保护成员变量)
注意:
(1)友元关系不具有传递性
(2)友元关系不具备双向性(我是你的友元类,我可以访问你,但你不可以访问我)
(3)若要互相访问对方类的成员,则要双方都要定义对方为自己的友元
(4)内部类就是外部类的友元类(内部类:如果一个类定义在另一个类的内部,这个内部类就叫做内部类)
//友元关系具有单向性,不具有交换性
//友元关系不能传递
//友元不能继承
class Time
{
friend class Date;
public:
Time(int hour = 0, int minute=0, int second=0)
:_hour(hour)
,_minute(minute)
,_second(second)
{
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
friend istream& operator>>(istream& _cin, Date& d);
public:
Date(int year = 2023, int month = 3, int day = 29)
:_year(year)
,_month(month)
,_day(day)
{}
ostream& operator<<(ostream& _cout)
{
_cout << _year << "-" << _month << "-" << _day << endl;
return _cout;
}
void Print()
{
cout << _year << endl;
cout << _month << endl;
cout << _day << endl;
}
void SetTimeof(int hour, int minute, int second)
{
_t._hour = hour;
_t._minute = minute;
_t._second = second;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
istream& operator>>(istream& _cin, Date& d)
{
_cin >> d._year;
_cin >> d._month;
_cin >> d._day;
return _cin;
}
int main()
{
Date d1;
d1.Print();
cin >> d1;
d1.Print();
return 0;
}
五、内部类
概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类
内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类
注意:内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象来访问外部类的所有成员。但外部类不是内部类的友元类。
特性:
1.内部类可以定义在外部类的public、protected、private都是可以的
2.注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名
3.sizeof(外部类)=外部类,和内部类没有任何关系
class Out
{
public:
class In
{
public:
void p(const Out& a)
{
cout << i << endl;
cout << a.j << endl;
}
};
private:
static int i;
int j;
};
int main()
{
Out o1;
Out::In I1;
}
六、匿名对象
匿名对象指的就是没有名字的对象,在使用中理解为实例化一个类对象,但是并不把它赋给一个对应的类变量,而是直接引用
我们知道在C++的创建对象是一个费时,费空间的一个操作。有些固然必不可少,但还有些对象是我们在不知道的情况下被创建的,通常以下三种情况会产生临时对象:
1.以值的方式给函数传参(传值拷贝)
2.类型转换:我们在做类型转换时,转换后的对象通常是一个临时对象。编译器为了通过编译会创建一个我们不易察觉的临时对象
3.传值返回:当函数需要返回一个对象,他会在栈中创建一个临时对象,存储函数的返回值
class A
{
public:
A(int a=0)
:_a(a)
{
cout << "A(int a)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
class Solution {
public:
int func(int n)
{
cout << "int func(int n)" << endl;
return n;
}
};
int main()
{
//有名对象
//A aa1;
//匿名对象
//生命周期只在当前一行
A();
A(10);
//A aa2(10);
//Solution sl;
//sl.func(10);
//Solution().func(1);
return 0;
}
七、拷贝对象优化
在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下是非常有用的