初始化列表
格式
尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使
用初始化列表初始化。
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 4)
{
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (NULL == _array)
{
perror("malloc申请空间失败!!!");
return;
}
_capacity = capacity;
_size = 0;
}
private:
DataType* _array;
int _capacity;
int _size;
};
class MyQueue
{
private:
// 声明
Stack _pushst;
Stack _popst;
int _size;
}
这里调用的是Stack的默认构造,但是把Stack(size_t capacity = 4)的=4删去,就没办法默认构造,MyQueue也无法生成默认构造。,所以要让MyQueue显示的写默认构造。这就是我们的初始化列表。
MyQueue(int n) //初始化定义
:_pushst(n)
,_popst(n)
,_x(1)
{
//内部函数
}
初始化列表格式:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
特征
初始化列表本质可以理解为对象成员定义的地方,所有的成员,可以在初始化列表中初始化,也可以在函数体内初始化
尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
但是有三种不能写在函数体内:
1.引用
2.const修饰
3.没有默认构造自定义类型
内置类型有缺省值用缺省值,没有缺省值看编译器,有的编译器会处理,有的不处理。
先走初始化,再走函数体
tip:缺省值是给初始化列表用的。
接下看这个程序
class A
{
public:
A(int a)
:_a1(a)
, _a2(_a1)
{}
void Print()
{
cout << _a1 << " " << _a2 << endl;
}
private:
int _a2;
int _a1;
};
int main()
{
A aa(1);
aa.Print();
}
这里我们发现声明顺序和定义顺序不一样
这里我们看见a2是随机值,这是因为成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
因为先声明的是a2,所以先进初始化列表的a2,但是此时的a1还未定义,所以是随机值。
隐式类型转换
特点
单参数的构造函数支持隐式类型转换
就是把内置类型转换为自定义类型
因为临时变量具有常性,所以在赋值时要用const去接收。
这里的3用完这一行就销毁。
如果是多参数的话,隐式类型转换这么写
const&A aaa={1,2,3};
这里有个优化:编译器遇到构造+拷贝构造的时候,会进行优化,优化为直接构造(前提是连续的一个表达式里)
编译器遇到拷贝构造+拷贝构造的时候,会进行优化,优化为一个拷贝构造
但是不是一个连续的表达式下,就无法满足了。
用处
那么这个隐式类型转换的用处什么时候体现呢?
class Stack
{
public:
void Push(A a)
{
// ......
}
};
int main()
{
Stack st;
A a1(1);
st.Push(a1);
return 0;
}
我们要用Push这个函数,先要构造函数,再进行拷贝构造,十分的麻烦。这时的隐式类型转换的优点就体现出来了。
class Stack
{
public:
void Push(const A&a)
{
// ......
}
};
int main()
{
Stack st;
st.Push(1);
return 0;
}
这里因为临时对象具有常性,所以Push要用const去接收。
这样提高了可读性,使用起来也很方便。
如果是多参数的话,隐式类型转换这么写
st.Push({1,2});
explicit关键字
如果不想要使隐式类型转换现象发生,就在单参数的构造函数前+explicit
static成员
静态成员变量
为什么这个静态成员变量报错了?
因为静态成员是在静态区,而缺省值是给初始化列表用的,静态成员不在初始化列表。
那要怎么定义他呢
静态成员函数
当我们把静态成员变量放在private下面,再用类域去拿就拿不到了。这时我们就要用到我们的静态成员函数。
静态成员函数没有隐藏的this指针,不能访问任何非静态成员
直接调用就行了。
友元函数
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
说明:
1.友元函数可访问类的私有和保护成员,但不是类的成员函数
2.友元函数不能用const修饰
3.友元函数可以在类定义的任何地方声明,不受类访问限定符限制
4.一个函数可以是多个类的友元函数
5.友元函数的调用与普通函数的调用原理相同
友元类:
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
1.友元关系是单向的,不具有交换性。
比如Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time
类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
2.友元关系不能传递
如果B是A的友元,C是B的友元,则不能说明C时A的友元。
内部类
概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意:内部类就是外部类的友元类,参见友元类的定义,**内部类可以通过外部类的对象参数来访问外部类中的所有成员。**但是外部类不是内部类的友元。
class A
{
private:
static int k;
int h;
public:
class B // B天生就是A的友元
{
public:
void foo(const A& a)
{
cout << k << endl;//OK
cout << a.h << endl;//OK
}
};
};
int A::k = 1;
特性:
1.内部类可以定义在外部类的public、protected、private都是可以的。
2.注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
3. sizeof(外部类)=外部类,和内部类没有任何关系