前言
C++中的类和对象(一:初识类和对象)https://blog.csdn.net/Outtch_/article/details/105413571
C++中的类和对象(二:六个默认成员函数)https://blog.csdn.net/Outtch_/article/details/105646143
经过了以上两个部分的学习,相信大家对类和对象有了一个初步认识,今天这篇博客再带大家深入了解一下类和对象。
初始化列表
我们之前想要在类创建时就对对象进行初始化时使用构造函数直接在构造函数中对成员变量进行赋值。但是这种方法并非是最好的方法,并且有一些情况我们就无法在构造函数中初始化,因此有了新的对成员进行初始化的方法——初始化列表。
初始化列表是和构造函数写在一起的,会在执行成员函数函数体之前优先利用初始化列表对成员变量赋予初值。
class Date{
public:
// 初始化列表
Date(int year = 0, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
- 特性
1.常成员,引用,没有默认构造函数的自定义类类型成员必须在初始化列表初始化。
2.类类型成员不论在初始化列表中是否显示初始化,都会自动在初始化列表中进行构造函数的调用。
3.初始化顺序与初始化列表无关,只与成员在类中声明先后有关。
4.每个成员在初始化列表中只能出现一次。
5.优先在初始化列表中初始化所有成员,尤其是类类型成员。
- explicit关键字
构造函数不仅可以构造与初始化对象,对于单个参数的构造函数,还具有类型转换的作用。
class Date{
public:
Date(int year)
:_year(year)
{}
explicit Date(int year)
:_year(year)
{}
private:
int _year;
int _month:
int _day;
};
void TestDate(){
Date d1(2018);
// 用一个整形变量给日期类型对象赋值
// 实际编译器背后会用2019构造一个无名对象,最后用无名对象给d1对象进行赋值
d1 = 2019;
}
上述代码的可读性不是很好,用explicit修饰构造函数,将会禁止单参构造函数的隐式转换。
static成员
用static修饰的成员变量称之为静态成员变量,用static修饰的成员函数称之为静态成员函数。
- 静态成员的特性
1.静态成员属于整个类和这个类实例化出的所有对象,而不是属于某个对象。
2.静态成员变量必须在类外进行初始化。
3.静态成员函数没有this指针,不能访问非静态成员变量。
4.非静态成员函数也可调用静态成员变量。
- 面试题:设计一个类计算类中实例化出了多少个对象
解题思路:
1.类实例化出对象只能通过构造函数和拷贝构造,再在这两个函数中分别设计一个计数器即可求解。
2.为了计数器的安全性(不能随意被修改),必须将这个计数器得放入类中用private限定。
3.这个计数器应该是全局属性的,所以在类中声明的时候加static修饰,表示计数器属于类,而不是属于某个对象。
4.静态成员变量的初始化必须在类外。
5.静态成员函数没有this指针,不能访问非静态成员变量。
具体实现:
#include<iostream>
using namespace std;
class A{
public:
A(){
++n;
}
A(const A& a){
++n;
}
// 修饰成员函数:没有this指针 函数中不能访问非静态成员
static int Getn(){
return n;
}
private:
// 修饰成员变量:解决安全问题
static int n;// 声明 n属于类 并不是属于某个对象 属于静态区
};
int A::n = 0; // 定义
A func(A a){
return a;
}
int main(){
A a1;
A a2;
func(a1);
func(a2);
cout << a1.Getn() << endl;
cout << a2.Getn() << endl;
cout << A::Getn() << endl;
return 0;
}
- static的作用
1.static修饰局部变量: 改变了局部变量的生命周期,使得变量出了作用域依然存在,直到程序结束变量的生命周期才结束。
2.static修饰全局变量: 改变了全局变量的链接属性,使得变量只在当前文件下可见。
3.static修饰普通函数: 改变了普通函数的链接属性,使得函数只在当前文件下可见。
4.static修饰成员变量: 使其变为静态成员变量。
5.static修饰成员函数: 使其变为静态成员函数,没有this指针。
为成员变量设置默认值(C++11)
这个语法只有在支持11版本的编译器上才可以使用,这种语法相当于给成员变量设定了默认值,十分类似于参数缺省。
class Date{
public:
Date(){
}
private:
int _year = 0;
int _month = 1;
int _day = 1;
};
这种设置默认值的语法如果我们并没有在构造函数中给成员变量赋值则会将其赋为这里的默认值。
注意:不适用于静态成员变量(静态成员变量必须在类外进行初始化)
友元
有的时候我们想要在外部使用一个对象的成员变量并且不想破坏其封装的时候就需要借助友元的语法:如果一个函数或者类是另一个类的友元,则这个函数或者类就可以自由地使用另一个类中的私有成员。
友元提供了一种突破封装的方式,有时增加了便利,但也会增加耦合度,破坏了封装,所以不宜多用。
友元函数
当写一个<< 的重载函数,如果写在类中则左操作数永远被我们的类本身占据(this指针),我们想要让ostream的对象作为左操作数的时候,函数就只能定义在类外,但这样就无法访问类中的私有成员,这时我们就可以借助友元来完成操作。
class Date{
//友元函数
friend ostream& operator<<(ostream& out, Date& d);
public:
void Print(){
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year = 0;
int _month = 1;
int _day = 1;
};
//不放在类中是因为有隐含this指针干扰
//返回ostream 解决了连续输出的问题
ostream& operator<<(ostream& out, Date& d){
out << d._year << "-" << d._month << "-" << d._day << endl;
return out;
}
int main(){
Date d1;
d1.Print();
cout << d1;
return 0;
}
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
- 特性
1.友元函数可访问类的私有和保护成员,但不是类的成员函数。
2.因为没有this指针,所以友元函数不能用const修饰(const修饰this指针指向的内容)。
3.友元函数可以在类定义的任何地方声明,不受类访问限定符限制。
4.一个函数可以是多个类的友元函数。
5.友元函数的调用与普通函数的调用和原理相同。
友元类
友元类和友元函数类似,一旦一个类是另一个类的友元,它就可以访问该类的私有成员。
class Date; // 前置声明
class Time{
// 声明日期类为时间类的友元类,则在日期类中就直接访问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;
};
- 特性
1.友元关系是单向的,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
2.友元关系不能传递,如果B是A的友元,C是B的友元,则不能说明C时A的友元。
内部类
内部类就是在一个类内部所定义的类。
class A{
private:
static int k;
int h;
public:
class 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;
}
- 特性
1.内部类是外部类的友元类。
2.外部类与内部类独立,外部类对内部类没有任何优越的访问权限。
3.对外部类取大小与内部类无关。
4.内部类可以不用加访问限定符就可以访问外部类的静态、枚举成员。