C++ -- 类和对象(下)
往期回顾
C++ – 类和对象(上)
C++ – 类和对象(中1)
C++ – 类和对象(中2)
1. 再谈构造函数
在前面的文章里我写到了构造函数的相关内容,现在我要进行补充。
1.1 初始化和赋值
在构造函数函数体内的操作都叫赋值,赋值是多次的。在类成员变量声明时可以给其缺省值,这个过程叫初始化,是只能进行一次的。除给予缺省值外,类中还有一种初始化叫初始化列表。
1.2 初始化列表
初始化列表的语法是在构造函数后,以一个冒号开始,逗号分隔成员变量,每个成员变量后的括号内放其初始值。
class AT {
public:
AT(int a,int t)
:_a(a)
,_t(t)
{ }
private:
int _a;
int _t;
};
1.3 初始化列表的细节
- 本质可以理解为成员变量定义的地方
- 类中的成员变量可以在列表位置初始化也可以在函数体内,但这三种情况必须在列表位置初始化:无默认构造函数的自定义类型成员、const成员变量、引用
class AT {
public:
AT(int a,int b)
:_a(a)
,_b(b)
,_st(10)
{
}
private:
int& _a;
const int _b;
Stack _st;
};
- 初始化列表中的初始化顺序即是成员变量的声明顺序,与位置无关。
class A
{
public:
A(int a)
:_a2(_a1)
,_a1(a)
{}
void Print()
{
cout << _a1 << " " << _a2 << endl;
}
private:
/*int _a2;
int _a1;*/ // 1 随机值
int _a1;
int _a2; // 1 1
};
int main()
{
A aa(1);
aa.Print();
return 0;
}
- 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。实际中的顺序是先初始化列表再函数体,且二者在效率上初始化列表更优。
- 成员变量的缺省值其实就是给初始化列表的。
1.4 隐式类型转换
我想过这样一个问题:内置类型是怎么变为自定义类型?
后来我明白了这归功于构造函数,构造函数可以做到隐式类型转换。
// 单参数构造函数
class A
{
public:
A(int a)
:_a(a)
{
cout << "A(int a)" << endl;
}
A(const A& aa)
:_a(aa._a)
{
cout << "A(const A& aa)" << endl;
}
private:
int _a;
int _a1;
int _a2;
};
int main()
{
A aa1(1);
A aa2 = aa1;
A aa3 = 3;
const A& raa = 3;
return 0;
}
aa1是构造函数,aa2是拷贝构造,aa3是拷贝构造3的临时对象,raa是引用3构造的临时对象。
这个过程中应该是先调用构造函数再调用拷贝构造函数啊,为什么运行后只有构造函数?其实是编译器优化了这个过程,编译器遇到连续的构造+拷贝构造会优化为直接构造。
说完单参数构造函数,来看看双参数构造函数
class A
{
public:
// 单参数构造函数
A(int a)
:_a(a)
{
cout << "A(int a)" << endl;
}
// 多参数构造函数
A(int a1, int a2)
:_a(0)
,_a1(a1)
,_a2(a2)
{}
A(const A& aa)
:_a(aa._a)
{
cout << "A(const A& aa)" << endl;
}
private:
int _a;
int _a1;
int _a2;
};
int main()
{
A aaa1(1, 2);
A aaa2 = { 1, 2 };
const A& aaa3 = { 1, 2 };
return 0;
}
只需要注意多参数需要用大括号包括。
1.5 explicit关键字
用explicit修饰构造函数,将会禁止构造函数的隐式转换。
class A
{
public:
explicit A(int a)
// ......
private:
int _a;
int _a1;
int _a2;
};
2. static成员
2.1 概念
用static修饰的成员变量叫静态成员变量,修饰的成员函数叫静态成员函数。
2.2 特征
- 必须在类外定义,类中的只是声明
- 可用 类名::静态成员 或者 对象.静态成员 来访问
- 静态成员存储在静态区而不在类内
- 声明时不能给缺省值
- 不在类内所以没有this指针,不能访问任何非静态成员
- 会受到public、protected、private 访问限定符的限制
// 体会static的声明和定义
class A {
public:
A()
{
++_scount;
}
A(const A& t)
{
++_scount;
}
~A()
{
--_scount;
}
static int GetACount()
{
return _scount;
}
private:
static int _scount;
};
int A::_scount = 0;
void TestA()
{
cout << A::GetACount() << endl;
A a1, a2;
A a3(a1);
cout << A::GetACount() << endl;
};
int main()
{
TestA();
return 0;
}
3. 友元
3.1 友元函数
- 友元函数可以访问类的所有私有和保护成员,但其不是类的成员
- 友元不能被const修饰
- 友元不能继承
- 一个函数可以是多个类的友元
- 友元可以定义在类内的任意位置,不受访问限定符限制
3.2 友元类
- 友元类的所有成员函数都是另一个类的友元
- 友元关系是单向的
- 不能继承
4. 内部类
4.1 概念
如果一个类定义在另一个类的内部,这个内部类就叫做内部类。
4.2 特征
- 内部类是外部类的友元
- 内部类可以定义在外部类的任意地方
- 内部类可以随意使用外部类的成员
5. 匿名类
不给对象名的对象就是匿名类,其特点是生命周期只在定义的这行,离开这行便会销毁。