类和对象——下篇
再谈构造函数
C++11 的成员初始化新玩法。
static成员
友元
内部类
1.再谈构造函数
1.1初始化列表
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括
号中的初始值或表达式。
- 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
- 类中包含以下成员,必须放在初始化列表位置进行初始化:
引用成员变量
const成员变量
类类型成员(该类没有默认构造函数
class B
{
public:
B(int b)//没有默认的构造函数(无参的构造函数)
:_b(b)
{
}
private:
int _b;
};
class A
{
public:
A(int a,int b)
:d(10),
dday(a),
n(b)
{
}
private:
const int n;
int& dday;
B d;
};
int main()
{
system("pause");
return 0;
}
1.2初始化列表的次序
假设将下列代码的_size和_array的顺序交换,择初始化_array数组时开辟空间的大小则会成为随机值
注意:成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
class Array
{
public:
Array(int size = 10)
:_array((int*)malloc(sizeof(int)*_size))
,_size(size)
{}
private:
int _size;
int* _array;
};
int main()
{
Array a[10];
system("pause");
return 0;
}
1.3 explicit关键字(针对单参数)
匿名对象:(匿名对象的生命周期只有那一行)
1.匿名对象应用
if (N > 10)
{
Date tmp(100);
_aa = tmp;
}
_aa = Date(100);
2.
Date(2019, 3, 6).Display();
注意:此关键字实际上就是为了阻止进行隐式类型转换,在一般的场景下不经常使用
我们来看下面的一个奇特的行为:
Date a1(10);
Date a2 = a1;//拷贝构造
Date a3 = 11;//隐式类型转换1.构造了一个匿名对象 2.拷贝构造给a3 3.优化
const Date a4 = 11;
加上后则无法通过编译:防止类型转换
explicit Date(int year = 2018, int month = 1, int day = 1)
:N(10)
{
this->_year = year;
this->_month = month;
this->_day = day;
}
多参数的c++11支持这种情况
Date a3 = {2019,3,2}
2.C++11 的成员初始化新玩法。
非静态成员变量,可以在成员声明时,直接初始化
class B
{
public:
B(int b)//此处可以不写缺省值,默认构造函数就是无参或者全缺省的构造函数(不需要传参就可以完成初始化的)
:_b(b)
{}
int _b;
};
class A
{
public:
void Print()
{
cout << a << endl;
cout << b._b << endl;
cout << p << endl;
}
private:
// 非静态成员变量,可以在成员声明时,直接初始化。
int a = 10;
B b = 20;
int* p = (int*)malloc(4);
static int n;
};
int A::n = 10;
int main()
{
A a;
a.Print();
system("pause");
return 0;
}
static关键字
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的
成员函数,称之为静态成员函数。静态的成员变量一定要在类外进行初始化
面试题:编写一个函数判断一共创建了多少个类的对象
class A
{
public:
A()
{
++rcount;
}
A(const A& d)
{
++rcount;
}
static int Get()//不加static则会创建一个对象调用函数,加上之后则可以得到准确的结果
{
return rcount;
}
private:
static int rcount;
};
int A::rcount = 0;
int main()
{
A a, b;
A c(a);
cout << A::Get() << endl;
system("pause");
return 0;
}
- 静态成员为所有类对象所共享,不属于某个具体的实例(成员共享)
- 静态成员变量必须在类外定义,定义时不添加static关键字(定义不加关键字)
- 类静态成员即可用类名::静态成员或者对象.静态成员来访问(::访问)
- 静态成员函数没有隐藏的this指针,不能访问任何非静态成员(没有this指针不可访问非静态成员)
- 静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值,
const修饰符等参数(和类普通成员相同)
面试题:求1+2+3+…+n
class Solution {
public:
class Sum
{
public:
Sum()
{
sum+=i;
i++;
}
};
int Sum_Solution(int n) {
i = 1;
sum = 0;
Sum arr[n];
return sum;
}
private:
static int i;
static int sum;
};
int Solution::i = 1;
int Solution::sum = 0;
4. 友元
友元分为:友元函数和友元类
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多
用。
4.1友元函数
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声
明,声明时需要加friend关键字。
若直接重载<<或者>>将不能使用正常顺序书写。
class Date
{
friend ostream& operator<<(ostream& _cout, const Date& d);
friend istream& operator>>(istream& _in, Date& d);
public:
Date(int year, int month, int day)
{
this->_year = year;
this->_month = month;
this->_day = day;
}
void Display()
{
cout << this->_month << this->_year << this->_day << endl;
}
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& _in, Date& d)
{
_in >> d._year;
_in >> d._month;
_in >> d._day;
return _in;
}
int main()
{
Date d(2018, 3, 2);
cin >> d;
cout << d;
system("pause");
return 0;
}
说明:
友元函数可访问类的私有成员,但不是类的成员函数(不是类成员)
友元函数不能用const修饰(const只能用来修饰this指针)
友元函数可以在类定义的任何地方声明,不受类访问限定符限制(随机定义)
一个函数可以是多个类的友元函数(多指向性)
友元函数的调用与普通函数的调用和原理相同(调用原理相同)
4.2 友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
友元关系是单向的,不具有交换性。
比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time
类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
友元关系不能传递
如果B是A的友元,C是B的友元,则不能说明C时A的友元。
class Date; // 前置声明
class Time
{
friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变
量
public:
Time(int hour, int minute, int second)
: _hour(hour)
, _minute(minute)
, _second(second)
{}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
void SetTimeOfDate(int hour, int minute, int second)
{
// 直接访问时间类私有的成员变量
_t._hour = hour;
_t._minute = minute;
_t.second = second;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
内部类
内部类就是外部类的友元类。注意友元类的定义,内部类可以通过外部类的对象参数来访问外部类中
的所有成员。但是外部类不是内部类的友元。
class A
{
private:
static int k;
int h;
public:
class B//B类相当于给外部类使用的
{
public:
void foo(const A& a)
{
cout << k << endl;//OK
cout << a.h << endl;//OK
}
};
};
int A::k = 1;
int main()
{
A::B b;
b.foo(A());
return 0;
}
A的大小计算与B的大小无关,上面的代码大小为4(静态成员变量不计算大小)
- 内部类可以定义在外部类的public、protected、private都是可以的。
- 注意内部类可以直接访问外部类中的static、枚举成员,不需要外部类的对象/类名。
- sizeof(外部类)=外部类,和内部类没有任何关系。