1.前言
类和对象到这里基本已经接近尾声,本篇文章主要介绍一些与类和对象有关的相关细节,在后续使用类和对象中也有可能用的到。
本章重点:
本篇文章重点讲解初始化列表,友元,匿名对象和类中的static成员,以及类中的内部类的概念。
2.初始化列表
在谈论初始化列表之前就要再次提及构造函数了:
在没有初始化列表时,一般我们构建对象都是通过构造函数来进行赋值操作,但是这并不是初始化的操作,这只是赋值的操作,赋值可以多次赋值,而初始化却只能进行一次。
这就引出了初始化列表的概念。
先上例子:
class Date
{
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day) //以上三行就是初始化列表,对象在初始化列表阶段就已经创建好了
{}
private:
int _year;
int _month;
int _day;
};
解释初始化列表的格式:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括 号中的初始值或表达式
初始化列表不仅可以像上面那样使用,也可以在{}里面对初始化的值进行赋值操作
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{
_year++;
_day--;
}
//或者这样
Date(int year, int month, int day)
: _year(year)
, _month(month)
{
_month = month;
}
2.1 初始化列表的作用
有些变量在初始化时必须对他赋初值:
例如:
引用成员变量
const成员变量
自定义类型成员(且该类没有默认构造函数时)然而在构造函数函数体中的赋值,不叫对变量初始化,用上面的类型会报错
所以此时必须用初始化列表
class B
{
public:
B(int a, int ref)
:_aobj(a)
,_ref(ref)
,_n(10)
{}
private:
A _aobj; // 没有默认构造函数的自定义类型
int& _ref; // 引用成员
const int _n; // const修饰成员
};
因此博主建议:尽量后续在使用类和对象的时候,都用初始化列表进行初始化。
因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使 用初始化列表初始化。2.2 初始化列表的小细节
很多小伙伴就问了,是不是初始化列表就是我成员变量初始化的顺序呢?
答案是不是得,成员变量初始化的顺序与初始化列表无关,只与你声明的顺序有关,哪个先声明哪个先进行初始化
上例子:
class A
{
public:
A(int a)
:_a1(a)
,_a2(_a1)
{}
private:
int _a2;
int _a1;
};
int main() {
A aa(1);
}
解释:会先声明_a2,再声明_a1
_a2初始化时_a1还是随机值,所以_a2就被初始化成了随机值,而_a1会被初始化为1!
补充对c++11缺省值的理解
成员变量声明时给的缺省值
实际上就是给初始化列表的!
当用户没有显示传参初始化时,编译器会用用户定义的缺省值
当用户显示传参后,缺省值失效,使用用户传的值初始化!
例:
class A
{
public:
A(int a)//没有显示传参就用缺省值初始化
:_a1(a)
,_a2(a)
{}
private:
int _a2 = 1;
int _a1 = 2;
};
}
3.static成员
概念:
-
声明为static的类成员称为类的静态成员
-
用static修饰的成员变量
称之为静态成员变量 -
用static修饰的成员函数
称之为静态成员函数 -
静态成员变量一定要在类外进行初始化
static成员的特性
-
静态成员为所有类对象共享,放在静态区
-
静态成员变量必须在类外定义,类内只是声明
-
类静态成员即可用
类名::静态成员
或对象.静态成员
访问 -
静态成员函数没有隐藏的this指针
不能访问任何非静态成员 -
静态成员受public、protected、private访问限定符的限制
例子:
class B
{
public:
static int Add(int x,int y);//没有this指针,无法访问类中成员
static int a;//在类中声明
};
int B::a = 10;//在类外定义
此类的所有成员共同享有这个静态变量
【问题】
1. 静态成员函数可以调用非静态成员函数吗?不能,因为使用静态成员函数不需要实例化出对象 2. 非静态成员函数可以调用类的静态成员函数吗?可以,静态成员函数他到底也是个函数,可以通过this指针来访问。
4.友元
假设一个函数我想定义在类外
但是我又想访问类中的私有成员
只能将私有成员改为共有再访问
这种操作就破坏了类的封装!
友元函数可以直接访问类的私有成员例:
它是定义在类外部的函数,不属于任何类
但需要在类的内部声明
声明时需要加friend关键字
class Date
{
friend int Add(int x,int y);//友元函数的声明
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
int Add(int x,int y)//友元函数的定义
{
x+=_year;
y+=_month-_day;
return x+y;
}
对友元函数的几点说明
- 友元函数可访问类的私有和保护成员
但不是类的成员函数 - 友元函数不能用const修饰
- 友元函数可以在类定义的任何地方声明
不受类访问限定符限制 - 一个函数可以是多个类的友元函数
- 友元函数的调用与普通函数相同
4.1友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。 友元关系是单向的,不具有交换性。 比如上述 Time 类和 Date 类,在 Time 类中声明 Date 类为其友元类,那么可以在 Date 类中直接访问 Time 类的私有成员变量,但想在Time 类中访问 Date 类中私有的成员变量则不行。 友元关系不能传递 如果 B 是 A 的友元, C 是 B 的友元,则不能说明 C 时 A 的友元。 友元关系不能继承,在继承位置再给大家详细介绍。 举例:class Time
{
friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成
员变量
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;
};
4.2 内部类
除了有友元函数可以访问类私有成员外
声明友元类也可以达到一样的效果
内部类就是友元类的典型代表!
ps:sizeof(外部类)的大小和内部类无关
上例子:
class A
{
private:
static int k;
int h;
public:
class B // B天生就是A的友元
{
public:
void foo(const A& a)
{
cout << k << endl;//OK
cout << a.h << endl;//OK
}
};
};
5.类的匿名对象
先定义一个类
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
使用匿名对象:
Date d = Date(2023,8,1);
这里的:Date(2023,8,1)就是匿名对象
- 生命周期只有一行
- 没有名字,在初始化或销毁时
自动调用构造或析构函数
6.explicit关键字
构造函数不仅能构造和初始化对象,对于单个参数或除第一个参数无默认值其余均有默认值的构造函数,还有隐式类型转换的作用 概念听起来好像有点迷魂,话不多说直接上例子
class Date
{
public:
Date(int year)
: _year(year)
{}
private:
int _year;
int _month = 1;
int _day = 1;
};
int main()
{
Date d = 1999;
return 0;
}
这里的Date d=1999就会有隐式类型转换
但是如果你加了explicit之后,就把隐式类型转换封住了,就不能使用了。那么就会报错。
只需要在构造函数前面+explicit即可,例
explicit Date(int year)
: _year(year)
{}
到这本文就结束了!
标签:初始化,--,成员,c++,month,int,year,day From: https://blog.csdn.net/weixin_62196764/article/details/139296150下期预告:c++内存管理