/*第七章 类*/
**类的基本思想是数据抽象和封装
**数据抽象是一种依赖于接口和实现分离的编程技术
**类的接口包括用户所执行的操作
**类的实现则包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数
7.1 定义抽象数据类型
7.1.1 设计Sales_data类
1. Sales_data接口函数
**isbn用于返回对象的bookNo编号
**combine对象加法
**add执行两个对象的加法
**read从is流读取数据到对象中
**print打印对象的值
7.1.2 定义改进的Sales_data类
struct Sales_data{
std::string isbn() const{return bookNo;}
Sales_data& combine(const Sales_data&);
double avg_price() const;
std::string boolNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
Sales_data add(const Sales_data&, const Sales_data&);
std::ostream &print(std::ostream&,const Sales_data&);
std::iostream &read(std::istream&, Sales_data&);
**成员函数的声明必须在类的内部
**接口组成部分的非成员函数的定义和声明都在类的外部
**定义在类内部的函数时隐式的inlie函数
1.引入this
**调用成员函数时,实际是在替某个对象调用它
**成员函数通过一个名为this的隐式参数来访问调用它的对象"用请求该函数的对象地址来初始化this"
**total.isbn() == Sales_data::isbn(&total); // *this=&total
**调用Sales_data的isbn成员时传入了total的地址,在成员函数内部,可以直接调用该函数的对象的成员
2.引入const成员函数
**紧随参数列表之后的const的作用时修改隐式this指针的类型
**this的类型是指向类类型非常量版本的常量指针,Sales_data*const需要遵守初始化规则-不能将this绑定到一个常量对象
**C++允许把const关键字放在成员函数参数列表之后,此时const表示this是一个指向常量的指针
**常量成员函数。常量对象以及其引用或指针都只能调用常量成员函数
3.类作用域和成员函数
**成员函数体可以随意使用类中成员而无须在意次序
4.在类的外部定义成员函数
**成员函数定义必须和声明严格匹配
**外部定义的成员名字必须包含所属类名
5.定义一个返回this对象的函数
**解引用this指针获得执行该函数的对象
7.1.3 定义类相关的非成员函数
**辅助函数,操作概念上属于类的接口组成部分,实际上不属于类本身
1.定义read和print函数
**IO类型的引用作为形参:IO类属于不能被拷贝的类型,只能通过引用传递
**执行输出任务的函数应该尽量减少对格式的控制,交给用户决定
2.定义add函数
7.1.4 构造函数
**类分别定义对象初始化的方式,构造函数是类通过一个或几个特殊的成员函数来控制其对象初始化的过程
**构造函数的任务是初始化对象的数据成员,创建类对象就会执行构造函数
**构造函数名字和类名相同
**构造函数没有返回类型,不能声明成const类型
**有参数列表
**可以包含多个构造函数但是不同构造函数之间必须在参数数量或者类型上区别开
1.合成的默认构造函数
**存在类内初始值,用它初始化成员
**默认初始化
2.不能依赖于合成的默认构造函数
**编译器只有在发现类不包含任何构造函数的情况下才会生成 //那么有的成员就没有被初始化
**对于某些类来说合成的构造函数可能执行错误的操作 //定义在块中的内置类型或者复合类型被默认初始化,值是未定义的
**编译器不能为某些类赫尔城默认的构造函数
**Sales_data() = default; //默认构造函数
**定义了其他函数,也必须定义一个默认构造函数
3.构造函数初始值列表
**Sales_data(const std::string &s):bookNo(s){ };
**Sales_data(const std::string &s,unisigned n,double p):
bookNo(s),units_sold(n),revenue(p*n){ };
**花括号定义了空的函数
**新部分:冒号以及冒号和花括号之间的->构造函数初始值列表
**为新创建的对象的一个或几个数据成员赋初值"初始值是成员名字的一个列表,每个名字后面紧跟括号括起来的成员初始值,不同成员初始化通过逗号隔开"
**当某个成员被构造函数初始值列表忽略,将以与合成默认构造函数相同的方式隐式初始化
4.在类的外部定义构造函数
**外部定义必须知名构造函树是哪个类成员
7.1.5 拷贝、赋值、析构
**定义类对象初始化之外,类还需要控制拷贝、赋值和销毁对象时发生的行为
**不主动定义这些操作,编译器将合成
1.某些类不能依赖于合成的版本
**当类需要分配类对象之外的资源时,合成的版本常常失效。例如管理动态内存的类通常不能依赖于合成版本
**很多需要动态内存的类能使用vector或者string对象管理存储空间(该类成员的合成版本能够正常工作)
7.2 访问控制与封装
**为类定义了接口,但并没有任何机制强制用户使用这些接口,类还没有封装,用户可以直达类对象的内部并且控制它的具体实现细节
1.使用访问说明符加强类的封装性:
**定义在public说明符之后的成员在整个程序内可被访问,public成员定义类接口
**定义在private说明符之后的成员可以被类的成员函数访问,但是不能被使用该类的代码访问,该部分封装了类实现的细节
2.使用struct和class的默认访问权限不一样
**使用struct关键字,则定义在第一个访问说明符之前的成员是public,class则相反
**当希望定义类的所有成员public时,使用struct,反之则使用class
7.2.1 友元
1.数据成员是private的,则辅助函数无法正常编译(不是类成员)
2.类可以允许其他类或者函数访问它的private成员:令其他类或者函数称为它的友元(friend)
**增加一条以friend关键字开始的函数声明即可
**友元声明只能出现在类定义的内部,不受所在区域访问控制级别的约束
7.3 类的其他特性
**类型成员、类的成员的类内初始值、可变数据成员、内联成员函数、从成员函数返回*this、关于如何定义类类型以及友元类的更多知识
7.3.1 类成员再探
**定义相互关联的类screen、windows_mgr
1.类可以自定义某种类型在类中的别名:'存在访问限制',可以是public或者private的一种
2.*'用来定义类型的成员必须先定义后使用,和普通成员不一样'
3.有另一个构造函数,默认构造函数必须存在
4.令成员作为内联函数:
**内部显式或者隐式
**外部用inline关键字修饰函数定义
5.重载成员函数
6.可变数据成员:一个mutable data member永远不会是const,即使是const对象的成员
**一个const成员函数可以改变一个可变成员的值
7.类内初始化的类元素:将默认值声明成一个类内初始值
7.3.2 返回*this的成员函数
1.函数定义的返回值不是引用,则返回值将是*this的副本,则调用只能改变临时副本而不能改变对象的值
2.一个const成员函数如果以引用的形式返回*this,返回类型将是常量引用
3.通过区分成员函数是否是const,对其进行重载
**非常量版本对于常量对象不可用,常量对象调用const成员函数,非常量对象调用非常量版本
7.3.3 类类型
**类定义了唯一的类型,即使两个类完全一样,类也是两个不同类型
**前向声明:引入类类型而不声明->不完全类型
**可以定义指向不完全类型的指针或引用
**可以声明以不完全类型作为参数或者返回类型的函数
**类允许包含指向它自身类型的指针或引用
7.3.4 友元再探
**类把普通非成员函数定义成友元
**可以将其他类定义成友元
**可以将其他类(已定义)的成员函数定义成友元
**友元函数可以定义在类内部,则函数是隐式内联的
1.类指定了友元类,则友元类的成员函数可以访问此类包括非公有成员的所有成员
2.友元关系不存在传递性
3.类可以只为其他类的成员函数提供访问权限
**必须明确指出成员函数属于的类
**friend void Window_mgr::clear(ScreenIndex);
**必须组织程序结构以满足声明和定义的依赖关系
4.重载函数声明成友元必须分别声明
5.类和非成员函数的声明不是必须在它们的友元声明之前
7.4 类的作用域
**在类的作用域之外,普通数据和函数成员只能由对象、引用或者指针使用成员访问运算符来访问
**一旦遇到类名,定义的剩余部分就在类的作用域之内
**函数的返回类型通常出现在函数名之前,因此当成员函数定义在类的外部,返回类型中使用的名字都位于类的作用域之外,因此'返回类型'
**必须指明它的类成员
7.4.1 名字查找与类的作用域
1.名字查找(lookup)
**在名字所在块中找声明语句(只考虑出现前的声明)
**没找到则查找外层作用域
**没有匹配声明,报错
2.类的定义处理
**编译成员的声明
**类全部可见后才编译函数体->成员函数体直到整个类可见后才会被处理,所以它能使用类中定义的任何名字
3.类型名要特殊处理
**内层作用域重新定义外层作用域的名字
**在类中,成员使用外层作用域中作为类型的名字,则类不能重新定义该名字
4.成员定义中的普通块作用域的名字查找
**成员函数参数会隐藏同名类成员,同名类成员可以通过指针、作用域运算符访问
5.成员定义在类外部,名字查找不仅考虑类定义之前全局作用域中的声明,同时考虑成员函数定义之前的全局作用域中的声明
**成员声明在类内,但是定义在类外部,此时类外部成员定义之前可能还有其他声明
7.5 构造函数再探
7.5.1 构造函数初始值列表
1.如果没有在构造函数初始值列表中显式初始化成员,则该成员将在构造函数体之前执行默认初始化
2.初始值有时必不可少
**引用、const
**成员属于类类型,该类没有定义默认构造函数
3.初始值列表只说明用于初始化的值而不限定初始化的具体执行顺序
**成员初始化顺序与类定义中出现顺序一致
**最好令构造函数初始值顺序与成员声明顺序一致,尽可能避免使用某些成员初始化其他成员
4.如果一个构造函数都提供了默认实参,实际也定义了默认构造函数
**'默认实参'
7.5.2 委托构造函数
**新标准扩展构造函数初始值的功能
**使用所属类的其他构造函数执行它自己的初始化过程
**当构造函数委托给另一个构造函数时,受委托的构造函数的初始值列表及其函数体先被执行,接着才会执行到委托函数的函数体
7.5.3 默认构造函数的作用'学习不够深入仔细'
1.对象被默认初始化或值初始化时自动执行默认构造函数
2.类必须包含一个默认构造函数以便在对象被默认初始化或者值初始化9时使用
7.5.4 隐式的类类型转换
**构造函数'只接受一个实参',则它实际上定义了转换为此类类型的隐式转换机制->转换构造函数:隐式创建类对象
1.编译器只会自动执行一步类型转换
2.通过将构造函数声明为explicit加以阻止隐式转换
**只对一个实参的构造函数有效
**类外部定义不应重复
**只能以直接初始化形式使用
3.对explicit构造函数显式强制进行转换
7.5.5 聚合类
**聚合类使得用户可以直接访问成员,并具有特殊的初始化语法形式
1.满足条件
**成员都是piblic
**没有定义任何构造函数
**没有类内初始值
**没有基类、没有virtual函数
2.提供花括号括起来的成员初始值列表初始化聚合类数据成员
**初始值顺序必须与声明顺序一致
**初始值列表元素个数绝对不能超过类的成员数量
7.5.6 字面值常量类
1.满足条件:
**数据成员都是字面值类型的聚合类是字面值常量类
**不是聚合类但是满足条件也是字面值常量类
2.字面值常量类的构造函数可以是constexpr函数,且必须至少提供一个constexpr构造函数
**必须初始化所有成员
7.6 类的静态成员
**类成员与类关联而不是与每个对象保持关联
**静态成员被所有类对象共享
1.通过在成员声明之前加上关键字static关联到类:'声明静态成员'
**静态成员可以是public或者private的
**可以是常量、引用、指针、类类型等
2.* 静态成员存在于任何对象之外,对象总不包含任何与静态数据成员有关的数据
* 静态成员函数不包含this指针,不能声明为const
3.访问静态成员:
**使用域运算符直接访问静态成员
**使用对象、引用、指针访问静态成员
**成员函数不用通过作用域运算符就可以直接使用静态成员
4.定义静态成员:在类的外部定义静态成员时,不能重复static关键字
**指向类外部的静态成员时,必须指明成员所属的类名
**静态数据成员不是创建对象时定义的,不是由构造函数初始化的->不能类的内部初始化静态成员
**必须在类的外部定义和初始化每个静态成员,只能被定义一次(赋值和定义的区别)
5.静态成员的类内初始化:为字面值常量类型的constexpr静态成员提供const整数类型的类内初始值(常量表达式)
**一个常量静态成员在类内部被初始化了,通常情况下类外部也应该定义一下该成员(不提供初始值)
6.静态成员和普通成员的区别:
**静态数据成员可以是不完全类型
**静态成员可以作为默认实参
*/
/****************************************************************************/
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Sales_data{
friend istream &read(istream &,Sales_data &);
friend Sales_data add(const Sales_data &,const Sales_data &);
friend ostream &print(ostream &,const Sales_data &);
public:
Sales_data()=default;
Sales_data(const std::string &s) : bookNo(s){ };
Sales_data(const std::string &s,unsigned n,double p) : bookNo(s),units_sold(n),revenue(p*n){ };
Sales_data(std::istream &is){
double price=0;
is>>(*this).bookNo>>(*this).units_sold>>price;
(*this).revenue = price*(*this).units_sold;
}
std::string isbn() const{
return bookNo;
}
Sales_data &combine(const Sales_data &rhs){
units_sold+=rhs.units_sold;
revenue+=rhs.revenue;
return *this;
}
double avg_price() const{
if(units_sold)
{
return revenue/units_sold;
}
else
{
return 0;
}
}
private:
std::string bookNo;
unsigned units_sold = 0;
double revenue=0.0;
};
istream &read(istream &is,Sales_data &item){
double price=0;
is>>item.bookNo>>item.units_sold>>price;
item.revenue = price*item.units_sold;
return is;
}
Sales_data add(const Sales_data &lhs,const Sales_data &rhs){
Sales_data sum=lhs;
sum.combine(rhs);
return sum;
}
ostream &print(ostream &os,const Sales_data &item){
os<<item.isbn()<<" "<<item.units_sold<<" "<<item.revenue<<" "<<item.avg_price();
return os;
}
class Screen{
public:
typedef std::string::size_type pos;
friend class Window_mgr;
Screen() = default;
//cursor执行了类内初始值初始化
Screen(pos ht,pos wd,char c):height(ht),width(wd),contents(ht*wd,c){
}
Screen(pos ht,pos wd):height(ht),width(wd),contents(ht*wd,' '){
}
char get() const{
return contents[cursor];
}
inline char get(pos r,pos c) const; //显式内联
Screen &move(pos r,pos c); //类外部显式内联
void some_member() const;
Screen &set(char);
Screen &set(pos,pos,char);
Screen &display(std::ostream &os){
do_display(os);
return *this;
}
const Screen &display(std::ostream &os) const{
do_display(os);
return *this;
}
private:
pos cursor = 0;
pos height = 0,width = 0;
std::string contents;
mutable size_t access_ctr = 0;
void do_display(std::ostream &os) const{
os<<contents;
}
};
inline Screen &Screen::move(pos r,pos c){
pos row = r*width;
cursor = row+c;
return *this;
}
char Screen::get(pos r,pos c) const{
pos row = r*width;
return contents[row+c];
}
void Screen::some_member() const{
++access_ctr;
}
inline Screen &Screen::set(char c){
contents[cursor] = c;
return *this;
}
inline Screen &Screen::set(pos r,pos col,char ch){
contents[r*width+col] = ch;
return *this;
}
class Window_mgr{
public:
using ScreenIndex = std::vector<Screen>::size_type;
void clear(ScreenIndex);
private:
std::vector<Screen> screens{Screen(24,80,' ')};
};
void Window_mgr::clear(ScreenIndex i){
Screen &s = screens[i];
s.contents = string(s.height * s.width, ' ');
}
class Debug{
public:
constexpr Debug(bool b = true): hw(b),io(b),other(b){ }
constexpr Debug(bool h,bool i,bool o): hw(h),io(i),other(o){ }
constexpr bool any() {
return hw||io||other;
}
void set_io(bool b){io=b;}
void set_hw(bool b){hw=b;}
void set_other(bool b){other=b;}
private:
bool hw;
bool io;
bool other;
};
/*exercise7.1*/
/* int main7_1()
{
Sales_data total;
if(cin>>total.bookNo>>total.units_sold>>total.revenue)
{
Sales_data trans;
if(cin >> trans.bookNo >> trans.units_sold >> trans.revenue)
{
if(total.bookNo == trans.bookNo)
{
total.units_sold+=trans.units_sold;
total.revenue += trans.revenue;
}
else
{
cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
total = trans;
}
}
cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
}
else
{
{
std::cerr << "No data!" << endl;
return -1;
}
}
return 0;
} */
/*exercise7.3*/
/* int main7_3()
{
Sales_data total;
if(cin>>total.bookNo>>total.units_sold>>total.revenue)
{
Sales_data trans;
if(cin >> trans.bookNo >> trans.units_sold >> trans.revenue)
{
if(total.isbn() == trans.isbn())
{
total.combine(trans);
}
else
{
cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
total = trans;
}
}
cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
}
else
{
{
std::cerr << "No data!" << endl;
return -1;
}
}
return 0;
} */
/*exercise7.4*/
struct Person{
friend istream &read_person(istream &,Person &);
friend ostream &print_person(ostream &,Person &);
public:
Person()=default;
Person(const std::string s,const std::string add):name(s),address(add){ };
Person(std::istream &is);
std::string client_name() const{
return name;
}
std::string client_address() const{
return address;
}
private:
std::string name;
std::string address;
};
istream &read_person(istream &is,Person &client){
is>>client.name>>client.address;
return is;
}
ostream &print_person(ostream &os,Person &client){
os << client.client_name() << " " << client.client_address();
return os;
}
Person::Person(std::istream &is){
read_person(is,*this);
}
/*exercise7.7*/
int main7_7()
{
Sales_data total;
if(read(cin,total))
{
Sales_data trans;
if(read(cin,trans))
{
if(total.isbn() == trans.isbn())
{
add(total,trans);
}
else
{
print(cout,total);
total = trans;
}
}
print(cout,total); //TODOdubug-average
}
else
{
{
std::cerr << "No data!" << endl;
return -1;
}
}
return 0;
}
/*exercise7.9:添加读取和打印person对象的操作*/
/*7.11:Sales_data类添加构造函数*/
int main7_11(){
Sales_data client(cin),client1("qinzhibao",10,23.5),client2("qinzhibao");
print(cout,client);
cout<<endl;
print(cout,client2);
cout<<endl;
return 0;
}
/*excercise7.27:检验类是狗正确*/
int main7_27(){
Screen myScreen(5,5,'X');
myScreen.move(4,0).set('#').display(cout);
cout<<"\n";
myScreen.display(cout);
cout<<"\n";
}
int main(void)
{
main7_27();
system("pause");
return 0;
}
“相关推荐”对你有帮助么?
-
非常没帮助
-
没帮助
-
一般
-
有帮助
-
非常有帮助
提交