C++ Primer(五)_OOP_数据抽象
数据抽象——类
类的基本思想——数据抽象(data abstraction)与封装(encapsulation)
数据抽象:一种依赖于接口(interface)与实现(implementation)分离的编程与设计技术
类的接口——用户所能执行的操作
类的实现——数据成员、负责接口实现的函数体及所需的各种私有函数
封装:实现类的接口与实现分离
封装的意义
- 防止用户代码对封装对象的封装
- 调整封装类的细节而无需调整用户代码
编译器对类的处理顺序
- 先编译所有成员声明
- 再编译所有成员实现——所以可以随意使用成员而无需在意顺序
构造函数
-
不可定义为const
const对象的常量属性的获得在构造函数完成初始化过程,所以const对象初始化期间可以修改
-
默认构造函数
- 无参数
- 或都有默认实参
-
未显示定义构造函数,编译器将合成默认构造函数
- 若有类内初始值,这个构造函数将初始化对应成员(类内初始值由编译器支持)
- 无初始值,执行默认初始化(有默认构造函数的类成员就默认构造,内置类型的默认初始化根据所在区域:全局0,局部未定义)——类内初始值以=或花括号形式赋予
-
委托构造函数——初始化列表调用其他构造函数——执行顺序为完全执行被委托者后返回委托者
-
初始化顺序——取决于类内声明顺序——与初始化列表无关——所以不要在初始化列表做一些依赖于顺序的事
成员函数的定义
类内定义——隐式inline
类外定义
-
inline可同时出现在内外
-
vitual只能出现在类内
-
const类内外都要出现——因为其修饰的是this指针所指,而底层const是可以形成重载的,所以如果不带const,会发现没这个成员
顶层const:修饰的是对象本身,对象本身不可变,初始化它的对象既可以是const也可以非const
void f(int); void f(const int); // 错误,重定义,两者不能形成重载 // 理由:函数匹配的实参转换等级中,1.实参形参类型相同;和2.向实参添加或删除顶层const都属于精确匹配(另一个精确匹配是实参数组或函数转为形参指针)底层const:变量自认为初始化的对象是不能改变的,初始化它的对象既可以是const也可以非const
但是,底层const能与非底层const形参能形成函数重载
void f(int *); void f(const int *); // 正确 // 理由:能对底层和非底层区分 // 1.底层const不能隐式转换为非const,即const int* 不能隐式转换为int*,首先对const int*实参就确定调用哪个 // 2.转换等级中:int *到const int *的转换等级比精确匹配低一级,所以对int *实参能确定调用哪个 -
=default可出现在类内也可出现在类外,且只能出现一处,类内为内联,仅用于拷贝控制成员
-
=delete,必须出现在第一次声明的时候,可用于各种函数
-
static只用于内部
const成员函数
-
const对象只能调用const成员函数
-
非const对象也能调用const成员函数
-
既定义const又定义非const的(既然非const对象能调用const成员,为什么费劲再写个const)一个理由:const修饰的是this指针所指,所以如果需要返回对象,const只能返回const对象,对非const对象略微便
-
const成员函数不能修改类成员,如若实在需要修改(如做些记录……):声明数据为mutable
一个现实的例子:
lambda表达式生成一个无名类的const临时对象,
而它的捕获列表实际是其类成员,
函数体实际是一个const成员函数,
所以如果lambda表达式需要修改捕获列表的值,就需要声明为mutable
类的前向声明——incomplete type
仅作声明而无定义:class Name;
因为定义,不知道有何成员,所以
-
定义该类型的指针或引用
-
亦可声明(不可定义——定义要在类定义之后)以其为参数或返回值的函数
class A; void test(A a); // 正确
友元——拥有与类内成员访问同样权限的对象
-
友元类
- 不可继承,即友元类不能随意访问派生
- 不可传递,即A为B友元,B为C友元,不能得到A为C友元
class A { friend class B; // 不要求声明于前 }; class B { }; -
友元函数
-
普通函数友元——友元声明仅指定访问权限,不是实际意义的函数声明,若希望类用户可调用友元。需要类外专门声明
虽然大多编译器不强制要求如此规则
// void f(); class A { friend void f() { /* 友元函数可在类内定义,但是即使是内部函数调用,也需要声明 */ } // 友元声明仅是隐式的假定f()可见,但事实这里并没有 // 错误,未声明 A() { f(); } // 若想要其正确,必须在A定义前声明f() };- 重载的函数需要每个声明为友元(如果有需要的话)
void f(); void f(int); class A { friend void f(); friend void f(int); }; -
成员函数友元——(这里假定这个函数是要访问类的,不然有何意义)
class B; // 不可定义,因为此时A并不可见——(如果是普通的函数到不在意) class A { public: void f(B); // 不可定义,因为B还未定义,不知其内容 }; class B { friend A::f(); }; void A::f(B) // 此时才知道B的内容 {}
-
-
友元模板
-
友元模板特例
-
甚至友元内置成员(虽然无实际用处,但允许这么做)

浙公网安备 33010602011771号