目录
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 ref)
:_aobj(a)
,_ref(ref)
,_n(10)
{}
private:
A _aobj; // 没有默认构造函数
int& _ref; // 引用
const int _n; // const
};
class Time
{
public:
Time(int hour = 0)
:_hour(hour)
{
cout << "Time()" << endl;
}
private:
int _hour;
};
class Date
{
public:
Date(int day)
{}
private:
int _day;
Time _t;
};
int main()
{
Date d(1);
}
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();
}
输出结果
1.3 explicit关键字
class Date
{
public:
// 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用
// explicit修饰构造函数,禁止类型转换---explicit去掉之后,代码可以通过编译
explicit Date(int year)
:_year(year)
{}
/*
// 2. 虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具
有类型转换作用
// explicit修饰构造函数,禁止类型转换
explicit Date(int year, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
*/
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
private:
int _year;
int _month;
int _day;
};
void Test()
{
Date d1(2022);
// 用一个整形变量给日期类型对象赋值
// 实际编译器背后会用2023构造一个无名对象,最后用无名对象给d1对象进行赋值
d1 = 2023;
// 将1屏蔽掉,2放开时则编译失败,因为explicit修饰构造函数,禁止了单参构造函数类型转
换的作用
}
2. Static成员
2.1 特点
1、静态成员变量必须在类外初始化,且定义时不加static但要加上类域(C++语法中,只有指针和整型的const static成员是可以在类中进行初始化的。)
2、静态成员存放于静态区,所有类对象共享这些静态成员,所以静态成员不占用类的内存空间。
3、类的公共静态成员可用类名::静态成员 或者 对象.静态成员来访问
4、静态成员函数没有隐藏的this指针,不能访问任何非静态成员
5、静态成员也是类的成员,生命周期是全局的,但是作用域受public、protected、private 访问限定符的限制
2.1注意事项
静态成员函数能调用非静态成员变量吗?
不能。
因为静态成员函数没有this指针,无法调用对象的普通成员函数。
普通可以调静态,静态调不了普通,原因是静态没有this。
3. 友元
友元破坏封装,尽量少用。
3.1友元函数
//类内声明为友元函数
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
//需要在类外定义
inline ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << " " << d._month << " " << d._day << endl;
return out;
}
inline istream& operator>>(istream& in, Date& d)
{
in >> d._year >>d._month >> d._day;
return in;
}
3.2友元函数的特点
1、友元函数可以访问类的私有和保护成员,但不是类的成员函数。
2、友元函数不能用const修饰
3、友元函数的声明可以放在类中的任意位置,它不受类访问限定符限制
4、一个函数可以是多个类的友元
3.3友元类
class Time
{
private:
friend class Date;//友元声明可以在类中的任意位置
int _hour;
};
class Date
{
public:
void GetTime()
{
cout<<_hour<<endl;//Date类是Time类的友元,这样Date就可以正常访问Time的私有和保护成员。
}
private:
int _year;
int _month;
int _day;
}
1、友元没有传递性,比如A是B的友元,B是C的友元,但是不能说A是C的友元。
2、友元关系不能继承。
5.匿名对象&&编译器优化
5.1匿名对象
1.匿名对象生命周期只有当前那一行
A a1;//有名对象,生命周期为当前栈
A a2(1);
A();//匿名对象,生命周期为当前行
A(1);
2.使用说明
class A
{
public:
A(int a = 0)
:_a(a)
{}
A Func()//3.使用匿名对象返回
{
return A(10);
}
private:
int _a;
};
int main()
{
A();//1.创建了个匿名对象
A().Func();//2.使用匿名对象调用成员函数
return 0;
}
5.2优化行为
1.单参数、多参数的隐式类型转换的优化
//单参数的构造,构造+拷贝,编译器直接优化为构造C++98
Date d1 = 2022;
//临时对象具有常性,这里就发生了一次构造,编译器没有优化空间了
const Date& d2 = 2023;
//多参数的构造C++11
Date d3 = { 2022,10,16 };
2.匿名对象传参时的优化写法
void Func(Date d)
{
}
int main()
{
//优化前
Date d1(2022);//构造
Func(d1);//传参发生拷贝构造
//优化后
Func(Date(2022));//使用匿名对象传参,构造+拷贝构造,编译器直接优化为构造
Func(2022);//隐式类型转换的优化,也是由构造+拷贝构造,编译器直接优化为构造
return 0;
}
3.编译器对返回值的优化
无优化
Date Func()
{
Date d(2022);//构造
return d;//return时拷贝构造一份临时对象
}
int main()
{
Date ret;
ret = Func();//使用返回的临时对象进行赋值
return 0;
}
初次优化:正常的流程如注释所示,需要构造+拷贝构造+拷贝构造,但是编译器会将其优化为构造+拷贝构造。
Date Func()
{
Date d(2022);//构造
return d;//return时拷贝构造一份临时对象
}
int main()
{
Date ret = Func();//使用返回的临时对象拷贝构造ret
return 0;
}
终极优化
Date Func()
{
return Date(2022);//使用2022构造一个临时匿名对象,return时再拷贝构造一份临时对象
}
int main()
{
Date ret = Func();//使用返回的临时对象进行拷贝构造
return 0;
}
这样写由构造+拷贝构造+拷贝构造,会被编译器直接优化为构造。是最优的写法。(因为匿名对象的生命周期只在这一行,所以只能传值返回,而不能传引用返回。)