类
类的简单定义:自定义的一个新类型和一个新作用域
构造函数
特殊成员函数,创建类类型的新对象都会执行。其工作就是保证每个对象的数据成员具有合适的初始值
其不能有返回值,可有0个或者多个不同形参:无形参(或者形参全都有默认实参)的构造函数叫默认构造函数
初始化方式:
1.构造函数初始化列表,一般应顺序初始化数据成员
constructor::constructor(const string &bookName):sBookName(bookName),dValue(0.0),iValue(0){}
列表的作用还可以用来初始化const类型、引用类型、未定义构造函数的类类型
eg:
class Screen{
public:
//使用默认实参100
Screen(int iVal=100):i(iVal),ri(i),a(ri){}
~Screen()
{
}
void print()
{
do_display(std::cout);
}
private:
inline void do_display(std::ostream &os)
{
os<<"i="<<i<<std::endl
<<"ri="<<ri<<std::endl
<<"a="<<a<<std::endl;
}
private:
int i;
const int ri;
const int &a;
};
tips: 实践证明在类中的内置类型不属于全局变量不会自动初始化为0,遵守变量初始化规则
2.一般函数类型
Screen()
{
}
默认构造函数(无形参,或者所有形参都提供默认实参)
只有当一个类没有一个构造函数时(这里指任意一个构造函数),编译器才会自动生成一个默认构造函数,即合成的默认构造函数
合成的默认构造函数同时也变量初始化的规则来初始化变量,所以如果有内置类型、复合类型(如指针,数组etc),应该自定义构造函数,而不应该用合成的默认构造函数
tips: 如果提供了其他构造函数,那么提供一个默认构造函数总是正确的
使用:
constructor obj=constructor();
隐式类类型转换
eg:
constructor(const std::string& str);
constructor(std::istream &is);
可以接受如下
constructor("oh");
constructor(std::cin); //但是请注意这都是临时构造的对象,使用完成之后就不可再访问,除非确定要这样使用,否则,可用explicit来抑制构造函数隐式转换
//下面就是在声明中声明为explicit抑制
explicit constructor(const std::string& str);
explicit constructor(std::istream &is);
在定义时不再使用explicit关键字
tips: 在单形参构造函数中,一般使用explicit关键字
同时如果想显式构造可以这么使用
constructor(std::string("oh,yes"));
类成员的显式初始化
struct className{
int i;
char *str;
};
可以如下初始化:
className obj={0,0};//这种是c形式,同时要求成员都为public,同时所有成员都要程序员自己初始化,添加删除也很麻烦
成员函数
在类内定义默认为inline类型
如果在类内声明为inline类型,在类外定义的时也写上inline,方便阅读
成员函数后加 const,作用为不可修改数据成员
mutable可以修改const成员函数中数据成员的值
数据抽象 & 封装
数据抽象是指设计类时,定义成员函数[接口]和数据成员的能力,而封装是指从常规访问中保护类成员的能力
数据抽象是依赖于接口和实现分离的编程技术,使用类的程序员只用抽象的知道类(接口)是做什么,而不必关心类内部的实现
类是封装的一个实体,代表若干成员的集合,设计良好的类隐藏了实现该类类型的成员
数据抽象和封装的好处:
避免类内部出现无意的,可能破坏对象状态的用户级错误
随时间的推移可以根据需求改变或缺陷报告来完善类实现,而无须改变用户级代码
this指针
普通非const成员函数返回的是 const *className 类指针 //className地址不能变,*className值可变
const成员函数返回的是const *const className
私有实用小函数
用于公共代码的私有实
用
小函数,应用于多个数据成员共用或者单个数据成员进一步分解成更小的函数
eg:
private:
do_display(std::ostream &os)
类成员访问
对象.成员 //这里的成员分别有数据成员和成员函数
指针->成员
类作用域
形参表和函数体处于类作用域中
函数返回类型不一定在类作用域中,如果是这样的话那就要写出完整的类型名
友元
1.友元类
eg:
class A{
public:
friend class B; //B是A的友元类,B可以使用所有A中的成员
private:
int _iVal;
};
2.其他类的友元函数
eg:
class A{
public:
friend B::Bfnc(A &a); // Bfnc 是A的友元函数,Bfnc可以使用所有A中的成员
private:
int _iVal;
};
3.普通函数作为友元函数
eg:
class A{
public:
friend fnc(A &a); // fnc 是A的友元函数,fnc可以使用所有A中的成员
private:
int _iVal;
};