类
1.定义抽象数据类型
类的基本思想就是数据抽象和封装。
struct Scales_data{
std::string isbn() const { return bookNo; }
Sales_data& combine(const Sales_data&);
double avg_price() const;
std::string bookNo;
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::istream &read(std::istream&, const Sales_data&);
成员函数
所有成员都必须在类内声明,但成员函数体可以定义在类内也可以定义在类外。
std::string isbn() const { return bookNo; } //在类内定义
引入this
isbn函数是如何获取bookNo成员所依赖的对象的呢?
通过this 隐式参数
std::string isbn() const { return this->bookNo; }
引入const
参数列表后和函数体之间加入const,作用就是修饰this指针的。未修饰之前,this的类型是Sales_data* const,修饰之后,this的类型是const Sales_data* const。
const 修改this 的指针类型,this 就成了指向常量的指针,既可以给它赋常量对象的地址,又可以赋非常量对象的地址。
如果this 不是指向常量的指针,就不能绑定到一个常量对象上。
使用const的成员函数称为常量成员函数。
常量对象,以及常量对象的引用和指针都只能调用常量成员函数。
在类的外部定义成员函数
名字必须包含它所属的类名
double Sales_data::avg_price() const {
if (units_sold)
return revenue/units_sold;
else
return 0;
}
函数声明在类内,一旦编译器看到这个函数名,就理解剩余的代码是位于类的作用域内的。所以可以使用units_sold等成员数据。
类相关的非成员函数
一般把这些函数的声明与类放在同一个头文件里,定义放另一个文件里。
构造函数
对象的初始化方式。只要对象被创建,就会执行构造函数。
构造函数不能被声明成const。
struct Scales_data{
Scales_data() = default;
Scales_data(const std:: string &s) : bookNo(s) { }
Scales_data(const std:: string &s, unsigned n, double p) :
bookNo(s) , units_sold(n), revenue(p*n) { }
Scales_data(std::istream &); //只声明了
std::string isbn() const { return bookNo; }
Sales_data& combine(const Sales_data&);
double avg_price() const;
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
}
Scales_data::Scales_data(std::istream &is) //构造函数
{
read(is, *this);
}
析构、赋值和拷贝
拷贝:初始化变量,值的方式传递,返回一个对象。
赋值:使用赋值运算符。
析构:销毁对象及所占空间。释放动态内存。
2.访问控制和封装
使用访问说明符加强类的封装性。
public之后的成员可以在整个程序内被访问,public类的成员定义类的接口。
private之后的成员可以被类的成员函数访问。
class Scales_data{
public:
Scales_data() = default;
Scales_data(const std:: string &s) : bookNo(s) { }
Scales_data(const std:: string &s, unsigned n, double p) :
bookNo(s) , units_sold(n), revenue(p*n) { }
Scales_data(std::istream &); //只声明了
std::string isbn() const { return bookNo; }
Sales_data& combine(const Sales_data&);
private:
double avg_price() const;
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
class 和struct的唯一区别:默认访问权限不一样。
友元
Scales_data的数据成员是private的,所以read、add、print 函数就不能访问了,类允许友元函数访问类的非公有函数。把一个函数作为友元,则增加一条以friend关键字开头的函数声明即可
class Scales_data{
//友元声明
friend Sales_data add(const Sales_data&, const Sales_data);
friend std::ostream &print(std::ostream&, const Sales_data&);
friend std::istream &read(std::istream&, const Sales_data&);
public:
Scales_data() = default;
Scales_data(const std:: string &s) : bookNo(s) { }
Scales_data(const std:: string &s, unsigned n, double p) :
bookNo(s) , units_sold(n), revenue(p*n) { }
Scales_data(std::istream &); //只声明了
std::string isbn() const { return bookNo; }
Sales_data& combine(const Sales_data&);
private:
double avg_price() const;
std::string bookNo;
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::istream &read(std::istream&, const Sales_data&);
最好在类开始或结束前集中声明友元。
友元的声明仅仅指定了访问的权限,而非通常意义上的函数声明。
我们还需在外面在声明一次。
3.类的其他特性
类型成员
除了定义数据成员和函数成员外,类还可以定义类型成员。也存在访问限制,可以是public和private中的一种。
class Screen{
public:
typedef std::string::size_type pos;
private:
pos cursor = 0;
pos height = 0, weight = 0;
std::string contents;
}
用户可以使用pos了,也可以
using pos = std::string::size_type;
可变数据成员
有时希望修改某个数据成员,即使是一个const成员函数内的。加入mutable关键字。
mutable size_t access_ctr; //数据类型前加上multable
类类型
即使两个类的成员列表完全一致,他们也是不同的类型。
友元再探
除了把非成员函数定义为友元函数,还可以把其他类定义成友元,也可以把其他类的成员函数定义成友元。
友元可以定义在类的内部,这样的函数是隐式内联的。
类之间的友元关系
class Screen{
friend class Window_mgr; //Window_mgr是Screen的友元类,Window_mgr可以访问Screen的私有部分
}
如果一个类指定了友元类,则友元类的成员函数可以访问此类包括非公有成员在内的所有成员。
友元关系不存在传递性。每个类负责控制自己的友元类或友元函数。
成员函数作为友元
class Screen{
//Window_mgr的成员函数clear作为Screen的友元函数
//Window_mgr::clear必须在Screen类之前被声明
firend void Window_mgr::clear(ScreenIndex);
}
一个类想把一组重载函数声明为它的友元,则需要对这组函数中的每一个分别声明。
友元声明与作用域
类和非成员函数的声明不是必须在他们的友元声明之前。就算在类的内部定义该函数,也必须在类的外部声明从而使函数可见。
struct X{
friend void f() { } //友元函数定义在类内部
X() { f(); } //错误,f 还没声明
void g();
void h();
}
void X::g() { return f(); } //错误,f 还没声明
void f(); //声明那个定义在X中的函数
void X::h() { return f(); }//正确,现在f 的声明在作用域中了