类3重点
1.const成员函数 和 取地址运算符重载
1.1const成员函数
这里定义了一个const对象,实际上const修饰的是该对象成员函数隐含的this指针.
this就是&d1,而this的类型规定为 Date* const this,意思是this指针本身不可修改,指向的对象可以修改,而从const Date d1 到this的过程,实际上是const Date* const this ,这个const类型的对象d1实际上是既不可以修改this指针本身,也不可以修改this的指向,传给成员函数默认情况是Date* const this,这就导致了权限的放大.
因此,这里需要一个const来修饰成员函数中隐含的this指针,但是这个this指针不能显示地传,在函数参数部分不可以写出来,所以规定这种写法:
void Date::Print() const
{
...
}
这个就叫做const修饰成员函数,const放在函数参数列表后边,如果有参数列表:
void Date::Print(...)
: ...(...)const
{
...
}
const 放在 * 前面代表这个指针指向的内容不可以改变,const 放在 * 后面代表这个指针本身不可以改变.
const 权限可以缩小不可以放大.
那么一个正常的 Date d1,也可以访问const成员函数,相当于是权限缩小.
所以建议不修改成员变量的成员函数把const加上.
1.2 取地址运算符重载
class Date
{
public :
Date* operator&()
{
return this;
// return nullptr;
}
const Date* operator&()const
{
return this;
// return nullptr;
}
private :
int _year ; // 年
int _month ; // ⽉
int _day ; // ⽇
};
上面两种成员函数都是取地址运算符的重载,区别就是const,实际上,取地址运算符重载不需要自己手动去写,因为这是默认成员函数之一,即使不手动写,&d1也会调用编译器默认的取地址运算符重载得到地址,并且尽可能匹配是否const,写这两个函数的情况在于如果不想让别人取到类对象的地址,可以修改重载函数内部逻辑.
2. 构造函数的初始化列表
2.1初始化列表
构造函数实际上是在定义对象的同时初始化成员变量,构造函数是一种初始化成员变量的方法,方法实在函数体内赋值.
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
{
public:
Date(int year,int month,int day)
:_year(year)
,_month(month)
,_day(day){}
private:
int _year;
int _month;
int _day;
};
对于引用,const修饰的变量,无默认构造的自定义类型,光使用构造函数是无法初始化的,就得使用初始化列表.
因为成员变量在类中属于声明而不是定义,语法上对于const修饰的变量和引用类型定义时必须初始化给值,否则报错,而构造函数是先声明后赋值初始化,这在语义上没有实现定义时就初始化,而对于类中的自定义类型,如果没有初始化给值就会走默认构造,如果同时没有默认构造就会报错.
class T
{
};
class Date
{
public:
Date(int year,int month,int day)
:_year(year)
,_month(month)
,_day(day)
,
{}
private:
int _year;
int _month;
int _day;
//三种必须使用初始化列表
//const int x;
//int& y;
//T t1;
};
同时,初始化列表也提供了缺省的使用,在声明成员变量的地方给缺省值,实际上是给初始化列表使用
class Date
{
public:
Date(int year,int month,int day)
:_year(year)
,_month(month)
,_day(day){}
private:
int _year = 1;
int _month = 1;
int _day = 1;
//缺省值,如果初始化列表中没有初始化,那么默认就是这里的缺省值
};
因此实际上初始化列表才算是真正的初始化成员变量的地方.
2.2初始化列表初始化顺序
注意: 初始化列表按照成员变量在类中声明的顺序进行初始化,与成员变量在初始化列表中出现的顺序无关,当然也建议这两者顺序保持一致.
下面程序的输出结果是什么??
#include<iostream>
using namespace std;
class A
{
public:
A(int a)
:_a1(a)
, _a2(_a1)
{}
void Print() {
cout << _a1 << " " << _a2 << endl;
}
private:
int _a2 = 2;
int _a1 = 2;
};
int main()
{
A aa(1);
aa.Print();
}
结果为 1, 随机值
3. 类型转换
- C++支持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数
- 构造函数前面加explicit就不再支持隐式类型转换
class A
{
};
int main()
{
// 1构造⼀个A的临时对象,再⽤这个临时对象拷贝构造aa3
// 编译器遇到连续构造+拷⻉构造->优化为直接构造
A aa1 = 1;
aa1.Print();
const A& aa2 = 1;
// C++11之后才支持多参数转化
A aa3 = { 2,2 };
return 0;
}
4.static成员
4.1static成员
- 用static修饰的成员变量叫做静态成员变量,静态成员变量在类里声明,类外初始化
class A
{
private:
static int Anum;//声明
};
int A::Anum = 0;//类外初始化
- 静态成员变量属于所有类对象,为它们所共享,不独属于某个具体对象,不存放在对象中,存放在静态区.
class A
{
private:
static int Anum;//声明
};
int A::Anum = 0;//类外初始化
int main()
{
A a;
cout << sizeof(a) << endl;//结果为1(证明静态成员变量不属于对象)
return 0;
}
- 用static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针
- 静态成员函数可以访问其他静态成员(函数,变量),但是不能访问非静态成员,因为没有this指针.
- 非静态成员函数可以访问所有成员,静态/非静态,函数/变量
4.2构造函数与析构函数的调用顺序
先定义的先初始化(构造);后定义的先析构
总体上先析构局部的,再析构全局的,static静态成员生命周期是全局
E B
5.友元
- 友元提供了一种突破类访问限定符封装的方式,友元分为:友元函数和友元类,在函数声明或者类声明的前面加friend,并且把友元声明放到一个类的里面。
- 外部友元函数可访问类的私有和保护成员,友元函数仅仅是⼀种声明,他不是类的成员函数。
- 友元函数可以在类定义的任何地方声明,不受类访问限定符限制。
- ⼀个函数可以是多个类的友元函数。
- 友元类中的成员函数都可以是另一个类的友元函数,都可以访问另一个类中的私有和保护成员。
- 友元类的关系是单向的,不具有交换性,比如A类是B类的友元,但是B类不是A类的友元。
- 友元类关系不能传递,如果A是B的友元, B是C的友元,但是A不是B的友元。
友元函数
#include<iostream>
using namespace std;
//前置声明,都则A的友元函数声明编译器不认识B
class B;
class A
{
// 友元声明
friend void func(const A& aa, const B& bb);
private:
int _a1 = 1;
int _a2 = 2;
};
class B
{
// 友元声明
friend void func(const A& aa, const B& bb);
private:
int _b1 = 3;
int _b2 = 4;
};
void func(const A& aa, const B& bb)
{
cout << aa._a1 << endl;
cout << bb._b1 << endl;
}
int main()
{
A aa;
B bb;
func(aa, bb);
return 0;
}
友元类
#include<iostream>
using namespace std;
class A
{
// 友元声明
friend class B;
private:
int _a1 = 1;
int _a2 = 2;
};
class B
{
public:
void func1(const A& aa)
{
cout << aa._a1 << endl;
cout << _b1 << endl;
}
void func2(const A& aa)
{
cout << aa._a2 << endl;
cout << _b2 << endl;
}
private:
int _b1 = 3;
int _b2 = 4;
};
int main()
{
A aa;
B bb;
bb.func1(aa);
bb.func1(aa);
return 0;
}
6.内部类
- 如果一个类定义在另一个类的内部,这个内部的类就叫做内部类,内部类是一个独立的类,跟外部的定义在全局的类相比,它只是受外部类域以及访问限定符的限制.
- 内部类默认为外部类的友元类,也就是内部类可以访问外部类的私有成员.
- 内部类实际上也是一种封装,如果两个类紧密相关,或者说一个类就是给另一个类所用的时候那么就可以考虑内部类的设计,类域外面使用的时候就要加类域,如果将该内部类限定为private或protected,那么这个内部类就是外部类的专属类,其他位置都用不了.
求1+2+3+…+n_牛客题霸_牛客网 (nowcoder.com)
class Solution {
public:
int Sum_Solution(int n) {
}
};
#include <regex>
class Solution {
private:
static int _i;
static int _num;
public:
class Sum{
public:
Sum()
{
_num += _i;
++_i;
}
};
int Sum_Solution(int n) {
Sum A[n];
return _num;
}
};
int Solution::_i = 1;
int Solution::_num = 0;
7.匿名对象
class A
{
A()
{
}
void print()
{
cout << "print" << endl;
}
};
int main()
{
//有名对象且调用函数->
A aa;
aa.print();
//匿名,生命周期只有一行,一行结束将会调用析构
A();
A().print();//只需要
A(1).print();
return 0;
}