目录
一、再谈构造函数
🍔构造函数体赋值
🍟初始化列表
二、explicit 关键字
三、static 成员
🍔概念
🍟特性
四、友员
🍔友员
🍟友元函数
🌮友元类
五、内部类
🍔概念
六、结语
一、再谈构造函数
🍔构造函数体赋值
创建对象时,编译器会通过构造函数给各个成员变量一个合适的初始值
class Date { public:Date(int year = 0, int month = 0, int day = 0){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day; };
通过构造函数,虽然每个对象都有了一个初始值,但这不是对象中成员变量的初始化。构造函数中的语句只能称为赋初值。
❓为什么这么说呢?
答:初始化只能有一次,而构造函数中可以多次赋值
下面我们就来看看对象中成员变量是如何初始化的⬇️
🍟初始化列表
🐥初始化列表:以一个冒号开始,接着是一个逗号分隔的数据成员列表,每个“成员变量”后
面跟一个放在括号里的初始值或表达式
class Date { public:Date(int year = 0, int month = 0, int day = 0):_year(year),_month(month),_day(day){}private:int _year;int _month;int _day; };
初始化列表【❗注意 ❗】
1️⃣每个成员变量只能在初始化列表中出现一次(只能初始一次)
2️⃣尽量使用初始化列表初始化成员变量,因为无论是否使用初始化列表,首先都会用初始化列表初始化
3️⃣成员变量在类中声明的次序就是初始化列表中的初始化顺序,与初始化列表中的先后无关
4️⃣类中包含一下成员,必须放在初始化列表中初始化
✅引用成员变量(因为引用必须在定义的时候初始化)
✅const成员变量 (因为常量只能初始化,不能赋值)
✅自定义类型成员变量(且该类没有默认构造函数)(因为使用初始化列表可以不调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化)
//没有默认构造的对象 class A { public:A(int a):_a(a){} private:int _a; };class B { public:B(int a, int ref):_ab(a),_ref(ref),_n(10){} private:A _ab; //没有默认构造函数int& _ref; //引用const int _n;// const };
二、explicit 关键字
如果一个对象的构造函数是单参数,我们可以这样来创建一个对象
class node { public:node(int val = 0):_val(val){} private:int _val; };int main() {node d1(1);d1=5;return 0; }
用一个整形变量给 node类型对象赋值
实际编译器会用 5 构造一个无名对象,然后用无名对象给d1对象赋值
👓当然,我们也可以用explicit关键字禁止这种行为
class node { public:explicit node(int val = 0):_val(val){} private:int _val; };int main() {node d1=5;return 0; }
编译失败:
三、static 成员
🍔概念
声明为static的类成员称为类的静态成员
因此有静态成员变量及静态成员函数
❗静态成员变量必须在类外进行初始化
class A { public:static int GetAcount()//静态成员函数{return _scount;} private:static int _scount;//必须在类外面进行初始化 }; int A::_scount = 0;
🍟特性
1️⃣静态成员为所有类对象共享,不属于某个具体对象,放在静态区
(也就是说用该类创建的所有对象使用的静态成员都是同一个)
2️⃣静态成员变量必须在类外定义,定义时不需要加static关键字,类中只是声明
3️⃣类静态成员可以用 类名::静态成员 或 对象.静态成员 访问
4️⃣静态成员函数没有隐藏的this指针,不能访问其他非静态成员
5️⃣静态成员也是类的成员,受public、protected、private访问限定符的限制
四、友员
🍔友员
友员提供了一种突破封装的方式,有时提供了便利。但是也会增加耦合度,破坏了封装的意义,所以友员不宜多用。
友员可分为:友元函数 和 友元类
🍟友元函数
🫗引出:当我们重载operator<<时,如果重载成成员函数,this指针默认就是第一个参数,也就是左操作数,但实际cout才是左操作数。cout<<d1 就会变成 d1<<cout 很奇怪
因此我们只能重载成全局函数,但又会导致类外无法访问类中的成员变量,此时就需要友元来解决⬇️
class Date { public:Date(int year = 0, int month = 0, int day = 0):_year(year),_month(month),_day(day){}//如果重载成成员函数//调用时 d1<<cout 不符合我们常规使用//ostream& operator<<(ostream& _cout)//{// _cout << _year << '-' << _month << '-' << _day;// return _cout;//}friend ostream& operator<<(ostream& _cout, const Date& d); private:int _month;int _year;int _day; };ostream& operator<<(ostream& _cout,const Date&d) {_cout << d._year << '-' << d._month << '-' << d._day;return _cout; }
📘【说明】
友元函数可以直接访问类的私有成员,是定义在类外的普通函数,不属于类,但需要在类的内部声明,并加上friend关键字
1️⃣友元类可以访问类的私有成员和保护成员,但不是类的成员函数
2️⃣友元函数不能用const修饰
3️⃣友元函数可以在类定义的任何地方声明,不受访问限定符的限制
4️⃣一个函数可以是多个类的友元函数
🌮友元类
友元类可以访问另一个类的所有非公有成员
👉友元关系是单向的
a类可以访问b类的非公有成员变量,但b类不可以访问a类中的非成员变量
👉友元关系不具有传递性
a是b的友员,b是c的友元,但不说明a是c的友元
class A { public:A(int a=0):_a(a){}friend class B; private:int _a; };class B { public:void set_ab(int a, int b){//直接访问A类中的私有成员变量__a._a = a;_b = b;} private:A __a;int _b; };
五、内部类
🍔概念
如果一个类直接定义在另一个类的内部,那么这个类就叫做内部类。
内部类是一个独立的类,它不属于外部类,外部类对内部类没有任何访问权限
但内部类是外部类的友元类,内部类可以直接访问外部类的非公有成员❗【注意】
1️⃣内部类可以定义在外部类的public、private、protected位置
2️⃣内部类可以直接访问外部类的static成员,不需要外部类的类/对象名
3️⃣sizeof(外部类)=外部类的大小,和内部类无关class A { public:A(int a=0):_a(a){}//内部类class B{public:void set_ab(int a, A& n){//对于static成员不需要加类/对象名k = 0;//可以访问私有成员变量n._a = 0;}};private:static int k;int _a; }; int A::k = 1;
六、结语
🫡你的点赞和关注是作者前进的动力!
🌞最后,作者主页有许多有趣的知识,欢迎大家关注作者,作者会持续更新有意思的代码,在有趣的玩意儿中成长!