1、再谈构造函数
1.1 构造函数体赋值
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
1.2初始化列表
class Date
{
public:
Date(int year, int month, int day)
:_year(year)
,_month(month)
,_day(day)
{}
private:
int _year;
int _month;
int _day;
};
class A
{
public:
A(int a)
:_a(a)
{}
private:
int _a;
};
class B
{
public:
B(int a, int b)
:_a(a)
, _b(b)
, _n(10)
{}
private:
A _a;// 没有默认构造函数
int& _b;// 引用
const int _n; // const
};
class A
{
public:
A(int a)
:_a1(a)
, _a2(_a1)
{}
void Print() {
cout << _a1 << " " << _a2 << endl;
}
private:
int _a2;
int _a1;
};
int main() {
A aa(1);
aa.Print();
}
声明次序就是初始化次序,所以应该是先初始化a2再初始化a1
那么打印的值就是1和随机值
1.3explicit关键字
构造函数不仅可以构造与初始化对象,对于单个参数或者多个参数的构造函数,还可以通过赋值类型转换的方式构造对象
class Date
{
public:
Date(int year)
:_year(year)
{}
Date(int year, int month, int day)
:_year(year)
,_month(month)
,_day(day)
{}
void Print()const
{
cout << _year << '/' << _month << '/' << _day << endl;
}
void Print()
{
cout << _year << '/' << _month << '/' << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
//C++支持单个参数的构造函数这样使用
Date d1 = 2023;//先用2023去创建临时对象,再用该对象去拷贝构造
d1.Print();
//支持多参数
Date d2 = { 2023,11,17 };
d2.Print();
//const要匹配
const Date d3 = { 2023,11,18 };
d3.Print();
return 0;
}
但是这样写可读性不好,可以用explicit关键字修饰该构造函数
加了explicit后就不允许这样构造对象了,不支持隐式类型转换了
2、static成员
2.1概念
假如我们要计算用这个类创建了多少个对象,构造函数调用(初始化)了几次
加上static之后,count就属于这个类
每个对象都能用它,但不支持缺省值
受访问限定符(private)限制,main函数不能直接调用count要写一个GetCount函数去返回count
using namespace std;
class A
{
public:
A(){++count;}
A(const A& t){++count;}
~A(){}
int GetCount()
{ return count; }
private:
static int count;//类里面声明
};
//类外面定义
int A::count = 0;
A func()
{
A aa;
return aa;
}
int main()
{
A aa;//有名对象
//为了调用GetCount而创建的所以count要-1
func();
cout <<aa.GetCount()-1<< endl;
cout << A().GetCount() - 2 << endl;//A()这种写法叫匿名对象,其生命周期只有在这一行
return 0;
}
还有一种更好的方法
直接将GetCount 写成静态成员函数(特点:没有this指针),这样就不用每次调用函数都要专门去新建一个对象了。那么静态成员函数该怎么调用呢?
类名 + 域作用限定符
cout<<A::GetCount()<<endl;
注意:由于没有this指针,静态成员函数无法访问其它非静态成员变量
using namespace std;
class A
{
public:
A(){++count;}
A(const A& t){++count;}
~A(){}
static int GetCount()
{ return count; }
private:
static int count;//类里面声明
};
//类外面定义
int A::count = 0;
A func()
{
A aa;
return aa;
}
int main()
{
A aa;
func();
cout <<A::GetCount()<< endl;
return 0;
}
总结:
静态成员函数和静态成员变量,本质是受限制的全局变量和全局函数,只不过专属这一个类
受类和访问限定符的限制
2.2特性
3、友元
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用
友元分为:友元函数和友元类
3.1友元函数
之前我们写全局函数,不得已将类的成员变量的限定访问操作符改成了公有(public)
而友元函数可以突破封装,就算类的成员变量是私有(private),友元函数也可以直接访问
这样就不用该限定访问操作符
它是定义在类外的普通函数,不属于任何类,但需要在类内声明,声明时要加friend关键字
using namespace std;
class Date
{
//友元声明
friend ostream& operator<<(ostream& _cout,const Date& d);
friend istream& operator<<(istream& _cin, const Date& d);
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
3.2友元类
假设有个类Date要经常访问另一个类Time的私有,那么可以加一个友元类的声明
friend class Date
声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量
class Time
{
friend class Date;
public:
Time(int hour, int minute, int second)
: _hour(hour)
, _minute(minute)
, _second(second)
{}
private:
int _hour;
int _minute;
int _second;
};
注意:友元关系是单向的,不具有交换性
友元关系不能传递
比如:C是B的友元,B是A的友元,但是C和A没有友元关系
4、内部类
using namespace std;
class A
{
public:
class B
{
public:
int _b;
};
private:
int _a;
};
int main()
{
cout << sizeof(A) << endl;
return 0;
}
结果是4,也就是不计算B的成员变量
B天生就是A的友元,B可以访问A,但是A不能访问B
A和B的关系: B是一个普通类,只是受A的类域和访问限定符的限制,使用B需要搜A的域
5、拷贝对象时的一些编译器优化
在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝
using namespace std;
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a)" << endl;
}
A(const A& aa)
:_a(aa._a)
{
cout << "A(const A& aa)" << endl;
}
A& operator=(const A& aa)
{
cout << "A& operator=(const A& aa)" << endl;
if (this != &aa)
{
_a = aa._a;
}
return *this;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
A f2()
{
A aa;
return aa;
}
int main()
{
A aa1(1);//构造
A aa2(aa1);//拷贝构造
A aa3 = aa1;//拷贝构造
return 0;
}
拷贝对象时的一些编译器优化
A aa1 = 1;
先用1构造一个临时对象;再用临时对象拷贝构造aa1
优化:同一个表达式中,连续的构造+构造 or 构造+拷贝构造 or 拷贝构造+拷贝构造 会合二为一
构造 + 构造 => 构造
构造 + 拷贝构造 => 构造
拷贝构造 + 拷贝构造 => 拷贝构造
A func2()
{
A aa;
return aa;
}
int main()
{
A aa(1);
A ret = func2();//连续拷贝构造会优化
func2();//不优化
return 0;
}