目录
[1 初始化列表](#1 初始化列表)
[2 类型转换](#2 类型转换)
[3 static成员](#3 static成员)
[4 友元](#4 友元)
[5 内部类](#5 内部类)
[6 匿名对象](#6 匿名对象)
1 初始化列表
构造函数的初始化除了在函数体内赋值,还有另一种方式,就是初始化列表。初始化列表使用时是以一个冒号开始,接着加逗号来分割数据成员列表。每个成员变量后面放括号,括号里放初始值或者表达式。 根据一段代码举例:
cpp
class Date
{
public:
Date(int& x, int year, int month, int day)
:_year(year) //初始化列表
, _month(month)
, _day(day)
{
}
void Print()const
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
每个成员变量在初始化列表中只能出现一次,初始化列表可以看做是每个成员变量定义初始化的地方。
注意 ,成员变量是引用成员变量 、const成员变量 、没有默认构造的类类型变量 ,必须放在初始化列表位置进行初始化,否则会编译报错。
C++11支持在成员变量声明的位置给缺省值,缺省值主要是给没有显示在初始化列表的初始化成员使用的。
举例说明:
cpp
class Time
{
public:
Time(int hour)
:_hour(hour)
{
}
private:
int _hour;
};
class Date
{
public:
Date(int& x, int year, int month, int day)
:_year(year)
, _month(month)
, _t(2)
, _ref(x)
, _n(1)
{
}
void Print()const
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day=1;//缺省值
Time _t;//没有默认构造的成员变量
int& _ref;//引用成员变量
const int _n;//const成员变量
};
无论是否写初始化列表,每一个构造函数都有初始化列表。尽量使用初始化列表初始化,因为**每个成员变量都会走初始化列表,即使这个成员变量不在初始化列表中。**如果成员变量在声明位置给了缺省值,初始化列表没有这个成员变量,会用这个缺省值初始化。
如果成员变量既没有给缺省值,又没有在初始化列表初始化,对于内置类型成员是否初始化取决于编译器,C++没有单独规定。对于自定义类型成员会调用这个成员类型的默认构造函数,如果没有默认构造函数会编译错误。
以下为初始化流程:

初始化列表中按照成员变量在类中的声明顺序进行初始化,跟初始化列表中成员出现的先后顺序没关系。建议初始化列表数据和声明顺序保持一致。看个例子:
cpp
#include<iostream>
using namespace std;
class A
{
public:
A(int a)
:_a1(a)
, _a2(_a1)
{
}
void Print()
{
cout << _a1 << " " << _a2 << endl;
}
private:
int _a2 = 2;
int _a1 = 2;
};
int main()
{
A aa(1);
aa.Print();
}
由于初始化列表中按照成员变量在类中的生命顺序进行初始化,所以_a2先初始化,此时_a1没有初始化,所以_a2打印出来是随机值,_a1为1。
2 类型转换
- C++支持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。
- 类类型的对象之间也支持隐式类型转换,需要相应的构造函数支持。
- 只要在构造函数前加explicit,就不支持隐式类型转换。
下面举个例子,有相应注释说明:
cpp
#include <iostream>
using namespace std;
class A
{
public:
A(int a)
:_a1(a)
,_a2(_a1)
{ }
private:
int _a1;
int _a2;
};
int main()
{
//构造
A aa1(1);
//隐式类型转换,由整型转换成A类型,用2构造一个临时对象,再用临时对象拷贝构造
//编译器会优化,会直接构造
A aa2 = 2;
//这样是错误的,2会产生一个临时对象,临时对象具有常性
//A& aa3 = 2;
//正确写法
const A aa3 = 2;
}
3 static成员
- 用static修饰的成员变量,称之为静态成员变量,静态成员变量要在类外进行初始化。
- 静态成员变量存放在静态区,为所有类对象所共享,不属于某个具体的对象。
- 静态成员函数没有this指针。
- 静态成员函数可以访问其他的静态成员,但是不能访问非静态的,因为没有this指针
- 非静态的成员函数,可以访问任意的静态成员变量和静态成员函数
- 静态成员也是类的成员,受public、protected、private访问限定符的限制。
- 静态成员变量不属于某个对象,不走初始化列表初始化。不能在声明位置给缺省值初始化。
cpp
// 实现⼀个类,计算程序中创建出了多少个类对象
#include<iostream>
using namespace std;
class A
{
public:
A()
{
++_count;
}
A(const A& t)
{
++_count;
}
~A()
{
--_count;
}
static int GetACount()
{
return _count;
}
private:
// 类⾥⾯声明
static int _count;
};
// 类外⾯初始化
int A::_count = 0;
int main()
{
cout << A::GetACount() << endl;//0
A a1, a2;
A a3(a1);
cout << A::GetACount() << endl;//3
cout << a1.GetACount() << endl;//3
return 0;
}
4 友元
友元提供了突破类访问限定符的方式,友元分为友元函数和友元类,使用方式是在函数声明或者类声明前面加friend,并且把友元声明放在一个类的里面。
外部的友元函数可以访问类的私有和保护成员,这是友元的作用。
cpp
#include<iostream>
using namespace std;
// 前置声明
class B;
class A
{
// 友元声明
friend void func(const A& aa, const B& bb);
private:
int _a1 = 1;
int _a2 = 2;
};
class B
{
// 友元声明
friend void func(const A& aa, const B& bb);
private:
int _b1 = 3;
int _b2 = 4;
};
void func(const A& aa, const B& bb)
{
cout << aa._a1 << endl;
cout << bb._b1 << endl;
}
int main()
{
A aa;
B bb;
func(aa, bb);//输出1 3
return 0;
}
- 一个函数可以是多个类的友元函数。
- 友元类的关系是单向的,A类是B类的友元,B类不是A类的友元。
- 友元类关系不可以传递,如果A是B的友元,B是C的友元,但是A不是C的友元。
- 友元会增加耦合度,破坏了封装,友元不宜多用。
5 内部类
如果一个类定义在另一个类的内部,这个类就叫做内部类。内部类是一个独立的类,外部的类实例化的对象不包含内部类
内部类默认是外部类的友元类
如果把内部类放在private或protected位置,那么这个内部类就是专属内部类,其余地方用不了。
cpp
#include<iostream>
using namespace std;
class A
{
private:
int _k=1;
int _h = 1;
public:
class B // B默认就是A的友元
{
public:
void f(const A& a)
{
cout << a._k << endl; //可以调用A的成员变量
cout << a._h << endl;
}
int _b1;
};
};
int main()
{
A::B b;
A aa;
b.f(aa);//输出1 1
return 0;
}
6 匿名对象
利用类型(实参)这样格式定义出来的对象是匿名对象,匿名对象的生命周期只有当前一行,过了这行就结束了。像类型 对象名(实参)这样格式定义出来的是有名对象
cpp
#include<iostream>
using namespace std;
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
class Solution
{
public:
int Sum_Solution(int n)
{
return n;
}
};
int main()
{
A aa1;
// 定义匿名对象,匿名对象的特点不⽤取名字,
// ⽣命周期只有这⼀⾏,下⼀⾏他就会⾃动调⽤析构函数
A();
A(1);
A aa2(2);
// 匿名对象在这样场景下就很好用,不用单独写一行去实例化新对象
cout << Solution().Sum_Solution(10)<<endl;
return 0;
}
以上就是本文全部内容了,主要说明类和对象中的各个小知识点,如果这篇文章对你有用,可以点点赞哦,你的支持就是我写下去的动力,本文是类和对象的最后一篇文章,后续会继续更新其他知识。