目录
1.再谈构造函数
1.1赋值构造函数
在类和对象(中)中我们讲到的下述代码是构造函数,但是他并不能称为将对象成员变量的初始化
class Data
{
public:
Data(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
构造函数中的语句只能称做为成员变量赋值,并不能称作定义或者初始化,因为初始化只能初始化一次,而赋值可以赋值多次。
1.2初始化列表
在构造函数名称后,第一个花括号前的列表称为初始化列表,以一个冒号:开始 .用,隔开每个成员变量,每个成员变量后面跟一个放在括号的初始值
实际上我们可以理解为:赋值构造函数前我们没有写初始化列表,实际上编译器帮我们已经默认初始化了,即已经定义了,其后才用定义的来赋值。
注意:以下成员变量必须用初始化列表
1.引用成员变量;2.const成员变量;3.自定义类型成员变量(没有默认构造函数)
很好理解,因为上述的成员变量定义时都必须给值,因此我们必须写初始化列表
class A
{
public:
A(int x)
{
}
private:
int x;
};
class B
{
public:
B( int n, int j, int i)
:_a(n)
,_y(j)
,_b(i)
{}
private:
A _a;
const int _y;
int& _b;
};
值得注意的是成员变量在类中的声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的顺序无关
1.3explicit关键字
我们观察下面图片,发现中间的Date d2= 2 ,构造时要经历许多步骤,称其为隐式转换
在c++中提供explicit关键字,当explicit修饰了构造函数过后,就不允许这种Date d2=2 (c++98)发生,当有多个参数时Date d3 ={2024,2,8}(c++11支持);
class Data
{
public:
explicit Data(int year=0)
:_year(year)
{}
private:
int _year;
int _month;
int _day;
};
2.static成员
2.1概念
static修饰的成员变量称为类的静态成员变量,用static修饰的成员函数称为静态成员函数。静态成员变量的定义一定要在类外进行,我们用 类名::静态成员变量/静态成员函数,来访问(当然在类外访问的成员变量只能是静态的)
2..2问题引入
如何计算一共创建了多少个类?
首先我们要明白,创建类就两种方式:1.构造函数2.拷贝构造函数
#include<iostream>
using namespace std;
int n = 0;
class Date
{
public:
explicit Date(int year = 0, int month = 1,int day =1)
:_year(year)
,_month(month)
,_day(day)
{
++n;
}
Date(const Date& d)
{
++n;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2;
Date d3(d2);
cout << n << endl;
return 0;
}
上述可以实现这个功能,但是我们发现n在外面可以被随意修改,失去了封装性。我们是不是可以将n声明在类中呢?
#include<iostream>
using namespace std;
class Date
{
public:
explicit Date(int year = 0, int month = 1,int day =1)
:_year(year)
,_month(month)
,_day(day)
{
++n;
}
Date(const Date& d)
{
++n;
}
static int GetN()
{
return n;
}
private:
int _year;
int _month;
int _day;
static int n;
};
int Date::n = 0;//初始化静态成员变量
int main()
{
Date d1;
Date d2;
Date d3(d2);
cout << Date::GetN() << endl;
return 0;
}
可见static修饰的成员变量不再属于单独某一个对象,而是所有对象共用。
static修饰的成员函数在外调用时,不用再通过对象调用,可直接通过类名调用。
2.3特性
a.静态成员为所有类对象共用,不再单独在某个对象中,而是存放在静态区;b.静态成员变量必须在类外定义,定义时可以不加static,声明咋类中 ;c.静态成员可以通过类名::静态成员或者对象.静态成员访问;d.静态成员函数没有隐藏的this指针,因此不能访问非静态的成员变量;d.静态成员也是类成员,受限定符限制
3.友元
3.1概念
友元提供了一种方式,突破了类的封装性,可以让某些函数和类访问类中的private的成员。
3.2友元函数
引入:
当我们如此定义<<,想完成对其的重载,实现cout<<d1的直接输出时发现问题,我们在类中无法正确的定义,原因是在<<操作符右边的cout会被默认为this指针,而我们要实现的类中的this指针确是指向d.我们调用时只能如此调用,那么就会失去可读性。
我们能否在类外定义呢?答案是肯定的,但是在类外定义了,我们如何访问类中的private成员呢?这时友元就发挥作用了。
#include<iostream>
using namespace std;
class Date
{
friend ostream& operator<<(ostream& out, const Date& d);
public:
Date(int year = 0, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{
}
void print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
/*void operator<<(ostream& out)
{
cout << _year << "/" << _month << "/" << _day << endl;
}*/
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& out,const Date& d)
{
cout << d._year << "-" << d._month << "-" << d._day << endl;
return out;
}
int main()
{
Date d1;
d1.print();
Date d2(2024);
cout << d1<<d2;
return 0;
}
当那个函数需要访问类private成员时,我们就将其声明放在类中,并加friend修饰
3.3友元类
当我们将A类中创建了另个B类对象b1时,想要访问b1中的private成员时,就要先将A类在B类中声明其为友元时,才可以在A类中访问b1对象中的private成员
class Time
{
friend class Date;
public:
Time(int hour = 0,int minute = 0,int secunde=0)
:_hour(hour)
,_minute(minute)
,_secunde(secunde)
{}
private:
int _hour;
int _minute;
int _secunde;
};
class Date
{
friend ostream& operator<<(ostream& out, const Date& d);
public:
Date(int year = 0, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{
}
void print()
{
_t._hour;
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
4.内部类
当我们在A类中定义了另一个B类时,我们就将B称为内部类,B类是A类天然的友元类,即A天生就可以访问B中的private成员
附图