今天我们来讲一些很散的东西,通过这些很散的东西我们可以使我们之前学的东西更加通透,基本上把这些知识搞定,类与对象的知识基本上就差不多掌握了。
初始化列表:
定义:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
这里我们不得再谈谈我们的构造函数了,我们通过一段代码来理解。
class A {
public :
A(int a=-1,int b=-20);
private:
int _a;
int _b;
};
A::A(int a, int b) :_a(10), _b(20)// 初始化列表的写法,其实就是写再构造函数上的
{
}
int main()
{
A a1(20,15);
return 0;
}
图解:
为证明这个说法,我们可以这样做:我们知道const修饰的数据只能在定义时赋值,不能在其他地方改变值,所以我们搞一个const修饰的数据。
放在初始化列表:
放在函数体内:
这足以证明,初始化列表是进行的定义,而函数体内进行的是成员的赋值。
其实我们不管有没有在初始化列表里定义成员,但是我们的初始化列表都会去过一遍每个成员,所以才使得,自定义类型回去调用他的默认构造,内置类型会让编译器来处理。
所以在这个代码中:
class A {
public :
A(int a=-1,int b=-20);
private:
int _a;
int _b;
const int _c;
};
class B {
public:
B(int d=100);
private:
A _q1;
A _q2;
int _d;
};
A::A(int a, int b) :_a(a), _b(b),_c(100)
{
}
B::B(int d )
{
}
int main()
{
A a1(20,15);
B b1;
return 0;
}
b1的初始化:
类的隐式转换:
注意我们的类也可以隐式转换。
我们举个简单的例子:
我们来简单的画一个图
这就非常的通透了。
小优化->有些编译器遇到连续构造和拷贝构造,会直接就构造了,意思是不会经历中间的过程。
举个例子:
class A {
public:
A(int a = -1);
A(const A& ad);
~A();
private:
int _a;
};
A::A(int a) :_a(a)
{
cout << " A::A(int a) :_a(a)" << endl;
}
A::~A()
{
cout << "A::~A()"<<endl;
_a = -1;
}
A::A(const A& ad)
{
cout << "A::A(A& a)" << endl;
_a= ad._a;
}
A Func()
{
A a=1;
return a;
}
int main()
{
A a2=Func();
return 0;
}
图解:
这里是vs2022的优化
explicit:
定义:修饰构造函数,使得不允许隐式转换
这里我们用张图简单带过:
此时就不能转换了,直接编译报错:
static修饰成员:
定义:声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化。
我们来看static五个特性:
1. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
比如我们要定义一个静态成员变量
声明:
定义:
2. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
因此我们可以在构造和拷贝里面加上_cout++用来观察生成了多少个同样的类
我们看这个代码:
class A {
public:
A(int a = -1);
A(const A& ad);
~A();
private:
int _a;
static int _cout;
};
int A::_cout = 0;
A::A(int a) :_a(a)
{
_cout++; //加一
}
A::~A()
{
_a = -1;
}
A::A(const A& ad)
{
_cout++; //加一
_a= ad._a;
}
int main()
{
A a1 = 1;
A a2 = 2;
A a3 = 3;
return 0;
}
图解:
3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
注意:静态成员也是类的成员,受public、protected、private 访问限定符的限制
所以我们将private换成public就可以进行访问
4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
这意味这只能访问静态成员。
访问非静态成员要报错。