默认成员函数除了:构造函数,析构函数,拷贝构造函数,运算符重载还有&符重载
默认成员都不能在类外定义,会默认生成。
&重载函数
A* operator&()
{
return this;
}
const A* operator&()const
{
return this;
}
//const 类实例和 普通实例都能调用。
&重载一般无特别大的意义,可以用来隐藏地址。此时&地址会返回空指针。
A* operator&()
{
return nullptr;
}
const A* operator&()const
{
return nullptr;
}
初始化列表
class object
{
private:
int _aa = 1;
};
class A
{
public:
A()
: _a(1)
, _b (_a)
, _c(1)
,aa()
{}
private:
int _a;
int& _b;
const int _c;
object aa;
};
初始化列表以冒号开始,间隔逗号。
初始化列表和构造函数体赋值都能用来初始化变量,初始列表可以用来初始化一些构造函数体不能赋值的变量。引用,const对象,自定义类型只能在初始化列表中初始化。
原因:引用,const 对象都必须在定义时初始化,而初始化列表相当于就是定义的地方,所以能进行初始化,函数体赋值更像时对初始化列表的2次修改。
那private 的变量不是定义吗?缺省值不是定义了吗?
不是,在private 相当于变量声明,缺省值相当于在初始化列表定义时,传入的值。所以,其实初始化列表始终存在,且先调用初始化列表,再调用函数体赋值。
三者的关系可以如下显示
int a;//private中声明
int a=1;//初始化列表
a=2;//函数体赋值
初始化列表可以替代函数体完成大部分工作,但还是需要函数体,如开辟动态内存后检测指针合法。推荐使用初始化列表初始化。
自定义类型为什么要在初始化列表中初始化?
因为如果编译器在定义自定义类型时无初始化就会报错,本质上和const对像一致。
自定义初始化类型的特性:初始化顺序和声明顺序保持一致
class A
{
public:
A()
: _a(1)
, _b (_a)
, _c(1)
{
}
private:
int& _b;
int _a;
const int _c;
object aa;
};
b的值是随机值,因为b的声明在a前,所以初始化列表先调用b的初始化,而忽略a=1;
explicit关键字来阻止隐式转换
class object
{
public:
object(int a)
{
_aa = a;
}
private:
int _aa=1;
};
//调用
object a1(1);
object a2=1;
object a1(1)是初始化,那么object a2=1是发生隐式转换
object a2=1->int转换为类隐式转换,产生临时变量,临时变量再调用拷贝构造初始化a2
object a2=1-> const object x(1)->object a2(x);
编译对这个过程进行优化,当一个表达式有多个构造时,编译器直接省略拷贝构造调用构造函数。
相当于object a2(1) ,不调用拷贝构造
证明存在临时变量:
object& b1 =1;
const object& b2=2;
第一行报错,第二行可以,说明存在临时变量,存在拷贝构造。
如果存在多个成员变量
class object
{
public:
object(int a = 1,int b=2)
{
_a = a;
_b = b;
cout << _a << _b << endl;
}
void fun2()
{
;
}
private:
int _a;
int _b;
};
object a = 3;
//结果为32
object a=3,4;//报错
隐式转换也只能初始化第一个成员变量。所以用隐式转换来代替构造,只能用于初始化一个成员变量时。
通过在构造函数前加explicit来阻止隐式转换。
class object
{
public:
explicit object(int a)
{
_aa = a;
}
private:
int _aa=1;
};
//调用
object a2=1;
//结果报错
类的static静态成员
静态成员变量
class object
{
public:
object(int a)
{
_aa = a;
}
private:
int _aa=1;
static int a;
};
//调用
object a1(1);
object a2=1;
//全局定义
int object :: a=0;
类定义静态变量是对全局变量的优化,类里面的静态变量只会定义有一次构造一次 。
类里面定义静态变量,成员变量属于每个类对象,静态变量属于每个类对象共享。不能在初始化列表初始化。
类的初始化只能在类外,定义在全局。
int object::a = 0;//定义时不要加static
静态变量依然受访问限定符限制,private和public的区别。
静态变量的运用场景:
统计实例个数时:在析构--,拷贝构造和构造函数++
静态成员函数
class object
{
public:
object(int a)
{
_aa = a;
}
static void fun1()
{
;
}
void fun2()
{
;
}
private:
int _aa=1;
static int a;
};
静态成员函数:fun1()在函数前加入static。fun2()不是静态成员函数。
静态成员函数特点,函数调用时不会传入this指针,这意味着不能在静态函数中调用非静态函数和非静态成员变量,但可以调用静态成员变量
static fun1()
{
fun2();
cout<< _aa<<endl;//报错
cout<<a<<endl;//可运行
}//报错
但是可以在非静态成员函数中调用静态成员函数。
fun2()
{
fun1();
}//可以使用
在类外可以用类名或者对象名访问静态函数。既然不需要this指针就能访问,所以可以用类名访问。
int main()
{
object a;
a.fun();
object.fun();
}
静态成员依然受范围限定符的限制。
静态函数的使用场景:只允许创建栈上的类
思路:把构造函数放在private中,创建一个public的静态函数返回创建的实例。这样才没有实例的情况下也能调用构造函数创造实例。