目录
1.构造函数再探
(1)初始化和赋值
初始化和赋值是不同的概念,当我们在定义变量时,可以先定义再赋值,或者定义并初始化,事实上先定义不初始化,编译器就会默认初始化变量,接下来再对变量的赋值操作不能称为初始化而是赋新值。显然,定义时并初始化的效率比先定义再赋值的更高,在类的构造函数中也是如此。
int a;//定义
a = 100;//赋值
int b = 200;//定义并初始化
(2)初始化列表和初始化顺序
在类中定义了构造函数,函数体内的为成员变量赋值的操作不是初始化。因为编译器在构造函数体执行之前就执行了默认初始化。例如:
但是有些成员必须在定义时就初始化它,比如引用,const或者没有默认构造函数的类类型成员。这样就不能在构造函数的函数体内进行初始化了。C++提供了唯一的给这些变量初始化并赋值的方式,这就是初始化列表。在构造函数的()后,{}之前写,格式是冒号+成员名(初始值),对与自定义类型则是调用它的构造函数初始化。
例如:
初始化顺序
成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。
例如:
在初始化列表中m_b在m_a前,但是m_a声明在m_b前,故先用m_b的默认初始化值初始化m_a。
(3)隐式类类型转换和explicit关键字
首先看下面例子,用100构造了对象aa。
可以这么理解,编译器用100构造了一个临时对象,再将这个临时对象拷贝构造aa,也就是将100隐式类型转换成了A类类型。对于这样连续的构造和拷贝构造的调用,编译器用寄存器优化了,只调用了一次构造函数。
转换规则也有限制,编译器只能支持一步的隐式类类型转换,例如:若要将”abc“转换成A类类型,就要先转换成string类型,再转换成A类型,进行两步的类类型转换,编译器不支持。可以显式类型转换来调用。
例如:
若类有能通过一个实参调用构造函数,则默认支持这个规则。要不让隐式类型转换发生,就要在类的对应构造函数前加上explicit关键字。
例如:
2.static成员
在类中,被static修饰的成员是类的静态成员,它们虽能被该类实例化的对象访问,但是它们是该类所有对象共有的成员。类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问。静态成员也是类的成员,受访问限定符的限制。
(1)静态成员变量
静态成员变量储存在静态区(数据段),用sizeof求类的大小时不计算在内,静态成员变量在类中只是声明,须在类外初始化,初始化时不加static关键字,但要加上类域(类名::)。类似于全局变量,静态成员变量只不过受了类域的限制,只能定义一次,生命周期直到程序结束。
(2)静态成员函数
因为静态成员函数没有隐藏的this形参,故不能访问非静态成员变量,也不能直接调用非静态成员函数,也不能是const成员函数,非静态成员函数则可以直接调用静态成员函数。
例如:用静态成员函数和静态成员变量计算类实例化了几个对象
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class A
{
public:
A(){static_var++;}
static void countObject()
{
cout << "There are " << static_var << " A Objects"<<endl;
}
static int static_var;//在类内声明
};
int A::static_var = 0;//必须在类外初始化,不带static关键字
int main()
{
A a1,a2,a3,a4,a5;
A::countObject();//直接用类名访问
A array[100];
A::countObject();
return 0;
}
输出:
3.友元和内部类
(1)友元函数
例如:要重载运算符<<,因为this占用了第一个形参位置,所以不能重载成成员函数,但是重载成全局函数访问不了类的私有成员,这时可以用友元解决,将全局函数在类中声明为友元函数即可访问私有成员。
注:
1.友元函数是定义在类外部的普通函数,不属于该类。
2.需要在类的内部声明,声明时需要加friend关键字。
3.友元函数不能用const修饰(非成员函数)。3.友元函数可以在类定义的任何地方声明,不受类访问限定符限制。4.一个函数可以是多个类的友元函数。5.友元函数的调用与普通函数的调用原理相同。
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class A
{
friend ostream& operator<<(ostream& out, A& a);
public:
A(int a = 1, int b = 2,int c=3)
:m_a(a),m_b(b),m_c(c)
{}
private:
int m_a;
int m_b;
int m_c;
};
ostream& operator<<(ostream& out, A& a)
{
out << a.m_a << "--"<<a.m_b << "--"<<a.m_c ;
return out;
}
int main()
{
A a;
A b(100,200,300);
cout << a << endl;
cout << b << endl;
return 0;
}
输出:
(2)友元类
将一个类声明为另一个类的友元,就是友元类,友元类的所有成员函数是另一个类的友元函数,都可以访问另一个类中的成员。
特性:
1.单向性,A是B的友元类,不代表B是A的友元类。2.友元关系不能传递,如果A是B的友元,B是C的友元,不代表A是C的友元。3.友元关系不能继承。
(3)内部类
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class A
{
public:
class inner
{
public:
void fun(A& a)
{
cout << a._a << endl;//内部类是外部类的友元类,可以访问私有成员
cout << s_var << endl;//内部类直接访问外部类的静态成员
}
private:
int _in=1;
};
private:
static int s_var;
int _a=10;
};
int A::s_var = 0;
int main()
{
cout<<sizeof(A::inner)<<endl;
cout<<sizeof(A)<<endl;//外部类的大小不含内部类
A a;
A::inner in;
in.fun(a);
return 0;
}
输出: