前言
博主水平有限,不足之处如能斧正,感激不尽!
通过前两篇类和对象的讲解,我们对类和对象有了大概的认识,此篇主要是补充类和对象的其他语法,主要有:
- 构造函数相关
- 初始化列表
- explicit关键字
- static成员
- const成员函数
- 友元
- 友元函数
- 友元类
- 内部类
- 匿名对象
一、构造函数相关
我们提过,构造函数体内对属性的“初始化”实际上是赋值,到了函数体内,属性已经定义好了。那我们怎么能给属性真正初始化呢?
这就需要用到初始化列表。
初始化列表
是什么
初始化列表是对象的属性定义的地方。
为什么
某些属性必须初始化,而不是定义后赋初值。
如
- 没有默认构造的自定义类型(需要传参构造)
- const常量
- 引用
特性
- 无论有没有显式写出初始化列表,调用构造函数的时候都会走一遍初始化列表
- 初始化的顺序是按照成员声明的顺序,不是按照写在初始化列表里的顺序
怎么用
className(datatype mem1, datatype mem2):_mem1(mem1),_mem2(mem2)
{}
函数体前,用冒号开头,通过括号给成员初始化,成员的初始化用逗号隔开。
class B
{
public:
B(int b)
{
_b = b;
}
private:
int _b;
};
class A
{
public:
A(const int a1, int& a2, int b):_a1(a1), _a2(a2), _bb(b)
{}
private:
const int _a1;
int& _a2;
B _bb;
};
int main()
{
int a1 = 10;
int a2 = 20;
int b = 30;
A aa(a1, a2, b);
return 0;
}
如果不适用初始化列表初始化:
建议
尽量都用初始化列表来初始化:用了一定没问题,不用可能有问题。
explicit关键字
单参构造在类型合适的时候,会被赋值运算符重载触发 类型转换 的功能。
听着很抽象,结合explicit来理解。
是什么
explicit 是用来 禁用函数自动类型转换 的关键字
为什么
有时我们不想要这样触发的类型转换
怎么用
explicit A()
{
//...
}
接下来就看看,有无explicit修饰的构造有什么区别。
class A1
{
public:
explicit A1(int a):_a(a)
{}
private:
int _a;
};
class A2
{
public:
explicit A2(int a):_a(a)
{}
private:
int _a;
};
int main()
{
//单参构造函数,用函数调用触发,不会发生类型转换
A1 aa1(10);
//单参数构造函数,用赋值运算符重载触发,会发生类型转换
A2 aa2 = 20;//error:No viable conversion from 'int' to 'A2'
return 0;
}
二、static成员
是什么
和整个类深度绑定的成员。
为什么
有些成员,需要能够被同类的所有对象访问。
特性
static成员:
- 和整个类深度绑定,不属于任何一个对象
- 受到访问限定符的限制
static成员变量
- 类内声明,类外定义初始化
- 在对象生成之前生成
static成员函数
- 指定类域即可调用
- 没有this指针(只能访问静态的成员)
怎么用
1. static成员变量:类内声明,类外定义初始化
class A
{
public:
A()
{
//...
cnt++;
}
A(const A& aa2)
{
//...
cnt++;
}
void Printcnt()
{
cout << cnt << endl;
}
private:
int _mem = 0;
//计算调用了几次构造和拷贝构造
static int cnt;
};
int A::cnt = 0;
A test(A aa)
{
return aa;
}
int main()
{
A aa1;
A aa2(aa1);
test(aa1);
aa1.Printcnt();
return 0;
}
:4
2. static成员函数
class A
{
public:
static void Print()
{
cout << "className: A" << endl;
}
private:
int _mem = 0;
};
int main()
{
A aa1;
A aa2;
A aa3;
aa1.Print();
aa2.Print();
aa3.Print();
return 0;
}
:className: A
className: A
className: A
问
【静态成员函数可以调用非静态成员函数吗?】
:不可以,没有this指针,非静态成员函数的第一个this形参接收不到。
【非静态成员函数可以调用静态成员函数吗?】
可以,静态成员函数属于整个类,类外指定类域即可调用,类内的成员函数更是可以直接调用。
三、const成员函数
是什么
this指针具有常性的成员函数。
为什么
带上const对象一起玩。
默认情况下,this的类型是 classname* const,尽管它是隐式传递的,也还是要遵循初始化规则,这也代表,当对象为常量对象,this指针传参会不匹配:
const classname* const 传给 classname* const
所以引入了const成员函数,
来声明此函数的this是const className* const。
怎么用
void Print() const
{
//...
}
放在参数列表后面,表示此成员函数的this指针为 const className* const this。
有什么用
- 提高传参灵活性
- 保护不用改变的对象
建议
不用改变对象的函数全声明成const成员函数。
四、友元
友元函数
有些时候,我们实现函数时,需要在类外访问类内的私有成员,但是又不想通过成员函数的形式,就有了友元。
是什么
是对非成员函数对类成员的访问权限的声明(声明后可以访问类内成员)。
虽然是满足了需要,但是这也破坏了类的封装,尽量少用。
为什么
有时需要允许特定的函数访问私有成员。
特性
- 仅仅声明了函数对类的访问权限,不声明具体函数
- 只有类的访问权限,不是类的成员函数(没有this)
- 定义在类内哪里都行
怎么用
class A
{
public:
friend void PrintMem(A& aa);
private:
int _mem = 0;
};
void PrintMem(A& aa)
{
cout << aa._mem << endl;
}
int main(int argc, const char * argv[])
{
A aa;
PrintMem(aa);
return 0;
}
:0
下图就印证了:友元函数的声明仅仅是声明了“函数的访问权限”,而非“对函数本身的声明”。
友元类
基本和友元函数一样。
特性
- 能直接访问外部类的static成员
- 友元关系是单向的
- 友元关系不能传递
- 友元关系不能继承(后面讲)
- 定义类内在哪里都行
class A
{
public:
friend class B;
private:
int _mem = 10;
};
class B
{
public:
void PrintA()
{
cout << _aa._mem << endl;
}
private:
int _mem = 0;
A _aa;
};
int main(int argc, const char * argv[])
{
B bb;
bb.PrintA();
return 0;
}
五、内部类
是什么
类中类,但它是独立的类。
特性
- 内部类是外部类的友元类
- 外部类不是内部类的友元类
- sizeof(外部类)就是外部类的大小,不包括内部类的大小
- 内部类定义在哪里都可以,但要指定类域
怎么用
class A
{
public:
class B
{
public:
void Print(A& aa)
{
cout << "内部类B访问外部类A的static成员:" << _a1 << endl;
cout << "内部类B访问外部类A的普通成员:" << aa._a2 << endl;
}
private:
int _b = 30;
};
private:
static int _a1;
int _a2 = 20;
};
int A::_a1 = 10;
int main()
{
A aa;
A::B bb;
bb.Print(aa);
return 0;
}
:内部类B访问外部类A的static成员:10
内部类B访问外部类A的普通成员:20
建议
破坏封装,尽量少用。
六、匿名对象
是什么
没有名字,只在当前行生效(下一行前自动调用析构)的对象。
为什么
有时候,我们实例化一个对象仅仅只是为了调用它的函数或其他简单的操作,这时候要实例化出一个普通对象,用完还要等到出生命周期才调用析构——不如弄个临时的用用。
怎么用
class A
{
public:
void Print()
{
cout << _aa << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _aa;
};
int main()
{
A().Print();
cout << "------------" << endl;
return 0;
}
:10
~A()
------------
今天的分享就到这里啦
这里是培根的blog,期待与你共同进步!
下期见~