类和对象
- static成员
- 概念
- static成员“登场”
- 特性
- static成员使用
- 注意
- explicit
- 从一段代码引入explicit
- 和explicit相关特性
- 友元
- 友元函数
- 引入
- 问题
- 解决
- 小结
- 友元类
- 内部类
- 概念
- 特性
- 匿名对象
- 引入
- 使用
static成员
概念
类的静态成员:声明为static的类成员。
静态成员变量:用static修饰的成员变量。
静态成员函数:用static修饰的成员函数。
注意:静态成员变量要在类外进行初始化
static成员“登场”
class A
{
public:
A(){
++_scount;
}
A(const A& t){
++_scount;
}
~A(){
--_scount;
}
//出现这个静态成员函数,是因为_scount是私有的,类外不能访问
static int GetCount()
{
return _scount;
}
private:
//非静态成员变量的默认值给初始化列表。
//静态成员变量没有初始化列表,所以不能给默认值。--> 没有this指针
static int _scount;
};
//全局定义,不能像成员变量一样在初始化列表定义
int A::_scount = 0;
int main()
{
cout << A::GetCount() << endl;
A a1, a2;
A a3(a1);
cout << A::GetCount() << endl;
return 0;
}
特性
- 静态成员被所有类对象所共享,不属于某个具体的对象,存放在静态区。
- 静态成员变量必须在类外定义,定义不添加static关键字,类中只是声明。
- 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问。
- 静态成员函数没有隐藏的this指针 ,不能访问任何非静态成员(成员变量和成员函数)。
- 静态成员也是类成员,所以受访问限定符限制。
static成员使用
//设计一个类,在类外面只能在栈和堆上创建对象
class A
{
public:
//在私有化构造函数的同时,在类中创建了两个静态成员函数,用来调用创建在栈和堆上的对象
//栈
static A GetStackObj()
{
A aa;
return aa;
}
//堆
static A* GetHeapObj()
{
return new A;
}
private:
//把构造函数私有化,因为创建对象就要调用构造函数
A(){}
private:
int _a1 = 1;
int _a2 = 2;
};
int main()
{
//static A aa1; //静态区
//A aa2; //栈区
//A* ptr = new A; //堆区
A* ptr = A::GetHeapObj();
cout << ptr << endl;
A::GetStackObj();
return 0;
}
注意
- 静态成员函数不能调用非静态成员。原因:没有this指针,无法在内部调用成员操作。
- 非静态成员函数可以访问类的静态成员。
- 静态变量也可以是自定义类型成员
成员变量和静态成员变量的区别:
成员变量:属于每一个类对象,存储对象里
静态成员变量:属于类,属于类的每个对象共享,存储在静态区。(特性第一条)
explicit
从一段代码引入explicit
class A
{
public:
A(int a)
:_a(a)
{}
private:
int _a;
};
int main()
{
A aa1(1);
A aa2 = 2;
}
代码分析:
和explicit相关特性
- 构造函数不仅可以构造和初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用。
- 无论单参还是除第一个参数无默认值其余均有默认值的 构造函数,被explicit修饰,该构造函数久不具备类型转换的作用
友元
提供了一种突破封装的方式,提供了便利的同时,破坏了封装。不宜多用
友元函数
引入
问题
在重载 <<(流插入)和 >>(流提取)运算符时,发现把该运算符重载成成员函数,不符合正常使用规则。因为cout输出流对象和隐含的this指针抢占第一个参数的位置。this指针默认是第一个参数(左操作数),但调用时cout需要是第一个形参对象。
eg:
class Date
{
public:
Date(int year, int month, int day)
:_year(year)
,_month(month)
,_day(day)
{}
//因为成员函数第一个参数一定是this,所以d必须放在左侧
//d << cout; -> d.operator<<(&d, cout); //但是这样不符合调用流插入操作符的常规
ostream& operator<<(ostream& _cout)
{
_cout << _year << "-" << _month << "-" << _day << endl;
return _cout;
}
private:
int _year;
int _month;
int _day;
};
解决
将 <<(流插入)和 >>(流提取)运算符重载成全局函数。但是会产生另一个问题,在类外没办法访问类里的成员。这时出现了友元函数。
class Date
{
friend ostream& operator<<(ostream& _cout, const Date& d);
friend istream& operator>>(istream& _cin, Date& d);
public:
Date(int year = 1900, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
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;
}
int main()
{
Date d;
cin >> d;
cout << d << endl;
return 0;
}
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明需要关键字friend
小结
- 友元函数可以访问类的私有成员和保护成员,但不是类的成员函数
- 友元函数不能用const修饰。原因:没有this指针
- 友元函数可以在类的任何地方声明,不受访问限定符限制
- 一个函数可以是多个类的友元
- 友元函数的调用和普通函数的原理一致
友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类的中的非公有成员。
class Time
{
friend class Date; //声明日期类是时间类的友元类。
public:
Time(int hour = 0, int minute = 0, int second = 0)
:_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;
};
在上述代码中,友元关系是单向的,Time类声明Date类为其友元类,所以Date类中直接访问Time类的私有成员,反过来,Time类不能访问Date类的私有成员。
内部类
概念
一个类定义在另一个类内部,这个在内部的类就是内部类。
注意:
- 内部类是一个独立的类,不属于外部类,不能通过外部类的对象去访问内部类的成员,外部类对内部类没有任何优越的访问权限。
- 内部类是外部类的友元类,内部类可以通过外部类的对象参数来访问外部类的所有成员。但外部类不是内部类的友元
特性
- 内部类可以定义在外部类的public、protected、private。但是内部类受外部类访问限定符限制。
- 内部类可以直接访问外部类的static成员,不需要外部类的对象或类名。
- sizeof(外部类) = 外部类。和内部类没有关系
eg:
class A
{
public:
class B //B天生就是A的友元
{
public:
void F(const A& a)
{
cout << k << endl;
cout << a.h << endl;
}
private:
int b;
};
private:
static int k;
int h;
};
int A::k = 1;
int main()
{
//静态变量不被计算在类的大小中。
//静态变量属于类不属于实列,无论创建多少个类的实列,静态变量始终只占用一块内存
cout << sizeof(A) << endl; //4
//B b; 不能直接这样定义
A::B b;
b.F(A());
return 0;
}
匿名对象
引入
匿名对象特征:
- 匿名对象不用起名字。
- 匿名对象的声明周期只有定义的一行,下一行他就会自动调用析构。
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a = 0)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
int main()
{
A aa1(1); //有名对象
A(2); //匿名对象
return 0;
}
使用
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a = 0)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
class Solution
{
public:
int Sum_Solution(int n)
{
return n;
}
};
int main()
{
//匿名对象使用场景
Solution().Sum_Solution(10);
//A& ra = A(1); //err 匿名对象具有常性
//使用const引用会延长匿名对象的生命周期,生命周期变成当前对象的局部域,
//相当于引用的生命周期
const A& ra = A(1);//ok
return 0;
}