一、再谈构造函数
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;
};
【注意】
1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
2. 类中包含以下成员,必须放在初始化列表位置进行初始化:引用成员变量、const成员变量、自定义类型成员(且该类没有默认构造函数时)
例如:
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
};
3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
4.成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。
例如:
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();
}
A.输出1 1
B.程序崩溃
C.编译不通过
D.输出1 随机值
1.3隐式类型的转换
1.3.1单参数
例如:
1.3.2多参数
例如:
int main()
{
A aa1(1,1); //构造函数
A aa2 = {2,2}; //隐式类型转换
return 0;
}
二、Static成员
类的静态成员包括静态成员变量和静态成员函数。
1.静态成员变量
类内声明,类外定义并初始化(声明是有static修饰,定义时没有static);不属于某个对象,属于所有对象,属于整个类;存放在静态区;若静态成员变量是私有的,则在类域外无法访问,若是共有的,则可以通过类名::变量名或者对象名::变量名进行访问。
例如:
class Date
{
//...成员函数
private:
static int count;//类内声明
int a;
int b;
};
int A::count = 0;//类外定义并初始化
注意:静态成员变量也是成员变量,除静态成员函数外的其他成员函数也有权对其进行访问或修改。
2.静态成员函数
函数形参没有隐藏的this指针,所以在静态成员函数中不能访问非静态成员变量;主要功能是对静态成员进行操作。
【问题】
1. 静态成员函数可以调用非静态成员函数吗?
2. 非静态成员函数可以调用类的静态成员函数吗?
三、友元
1.友元函数
定义:friend 关键字修饰的函数,是特殊的全局函数,可以访问其声明的类域的类的成员变量。
友元函数可以访问类内的所有成员变量,破坏了类的封装性,必要时可以使用,但尽量少用。
问题:现在尝试去重载operator<<,然后发现没办法将operator<<重载成成员函数。因为cout的
输出流对象和隐藏的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作
数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以要将operator<<重载成
全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。operator>>同理。
例如:
Class Date
{
//<< 函数重载
friend ostream& operator<<(ostream& _cout, const Date& d);
//>> 函数重载
friend istream& operator>>(istream& _cin, Date& d);
private:
int _year;
int _month;
int _day;
};
//<< 函数重载
ostream& operator<<(ostream& _cout, const Date& d)
{
_cout << d._year << '-' << d._month << '-' << d._day;
return _cout;
}
//>> 函数重载
istream& operator>>(istream& _cin, Date& d)
{
_cin >> d._year >> d._month >> d._day;
return _cin;
}
2.友元类
友元类内的所有成员函数都可以访问类内的成员变量,尽量不要使用。
特点:①友元关系不具对称性(单向)例如:A是B的友元类,B不是A的友元类
②友元关系不具传递性 例如:A是B的友元类,B是C的友元类,但A不是C的友元类
③友元关系不可继承
四、内部类
一个类的定义在另外一个类内,如:类B的定义在类A内,则类B是一个内部类。
特点:(以上例为例)
内部类B和外部类类A是两个独立的类,此处的独立是指物理空间的独立;
内部类B受外部类A的类域限制;
如果内部类B是外部类A的私有成员,则在类外不可以访问内部类B。
内部类B天生是外部类A的友元类,B的成员函数可以访问A的所有成员变量。
例:
class A
{
public:
A(int a = 1) :_a(a) {}
class B
{
public:
B(int b = 2) :_b(b) {}
void Print_A(const A& a)
{
cout << a._a << endl;
}
private:
int _b;
};
private:
int _a;
};
int main()
{
A aa;
//内部类和外部类物理空间独立
cout << sizeof(aa) << endl;//4
A::B bb;
//内部类天生是外部类的友元类,可以访问外部类的成员变量
bb.Print_A(aa);//1
return 0;
}
五、匿名对象
匿名对象,顾名思义,即没有名字的对象。
特点:1.匿名对象的生命周期只有一行代码,执行完后就销毁
2. 特殊情况下,有的类对象只需使用一次,则可以使用匿名对象(类比一次性物品来理解)
格式:类名()
例如:
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 Sum_Solution(int n)
{
//...
return n;
}
};
int main()
{
A aa1;
//A aa1();// 不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义
A();// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,
// 但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数
A aa2(2);
Solution().Sum_Solution(10);// 匿名对象在这样场景下就很好用,当然还有一些其他使用场
景,这个我们以后遇到了再说
return 0;
}
六、拷贝对象时的一些编译器优化
在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还是非常有用的。
1.构造+拷贝构造====优化====>构造
2.拷贝构造+拷贝构造====优化====>拷贝构造
3.构造+拷贝构造+拷贝构造====优化====>构造
例如:
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;
};
void f1(A aa)
{}
A f2()
{
A aa;
return aa;
}
int main()
{
A aa1;
f1(aa1);//传值传参
cout << endl;
f2(); // 传值返回
cout << endl;
f1(1);// 隐式类型,构造+拷贝构造->优化为直接构造
f1(A(2));// 一个表达式中,构造+拷贝构造->优化为一个构造
cout << endl;
A aa2 = f2();// 一个表达式中,拷贝构造+拷贝构造->优化一个拷贝构造
cout << endl;
aa1 = f2();// 一个表达式中,拷贝构造+赋值重载->无法优化
cout << endl;
return 0;
}