C++ Primer Chapter7 类

119 阅读5分钟

第七章 类

声明: 本文为《C++ Primer 中文版(第五版)》学习笔记。 原书更为详细,本文仅作学习交流使用,未经授权禁止转载。

在公众号【Jacen的技术笔记】,回复 C++,即可获得 两万字C++ Primer 要点整理PDF。

P228-P273

类的基本思想是数据抽象和封装。

抽象是一种依赖于接口和实现分离的编程技术。

封装实现了类的接口和实现的分离。

7.1 定义抽象数据类型

(1)this

任何对类成员的直接访问都被看作this的隐式引用。

std::string isbn() const {return bookNo;}

等价于

std::string isbn() const {return this->bookNo;}

(2)在类的外部定义成员函数

类外部定义的成员的名字必须包含它所属的类名。

double Sales_data::avg_price() const {
	if (units_sol)
		return revenue/units_sols;
	else
		return 0;
}

(3)构造函数

定义:类通过一个或几个特殊的成员函数来控制其对象的初始化过程,这些函数叫做构造函数。

构造函数没有返回类型;

构造函数的名字和类名相同。

类通过一个特殊的构造函数来控制默认初始化过程,这个函数叫做默认构造函数

编译器创建的构造函数被称为合成的默认构造函数

::: tip

只有当类没有声明任何构造函数的时,编译器才会自动的生成默认构造函数。

一旦我们定义了一些其他的构造函数,除非我们再定义一个默认的构造函数,否则类将没有默认构造函数

:::

7.2 访问控制与封装

(1)访问控制

说明符用途
public使用public定义的成员,在整个程序内可被访问,public成员定义类的接口。
private使用private定义的成员可以被类的成员函数访问,但是不能被使用该类的代码访问,private部分封装了类的实现细节。

(2)友元

类可以允许其他类或者函数访问它的非公有成员,方法是令其他类或者函数成为它的友元。

以friend关键字标识。

友元不是类的成员,不受访问控制级别的约束。

::: tip

友元的声明仅仅制定了访问的权限,而非通常意义的函数声明。必须在友元之外再专门对函数进行一次声明。

:::

// Sales_data.h

class Sales_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&, Sales_data&);
}

// nonmember Sales_data interface functions
Sales_data add(const Sales_data&, const Sales_data&);
std::ostream &print(std::ostream&, const Sales_data&);
std::istream &read(std::istream&, Sales_data&);

//Sales_data.cpp

Sales_data 
add(const Sales_data &lhs, const Sales_data &rhs)
{
	Sales_data sum = lhs;  // copy data members from lhs into sum
	sum.combine(rhs);      // add data members from rhs into sum
	return sum;
}

// transactions contain ISBN, number of copies sold, and sales price
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;
}

ostream&
print(ostream &os, const Sales_data &item)
{
	os << item.isbn() << " " << item.units_sold << " " 
	   << item.revenue << " " << item.avg_price();
	return os;
}

7.3 类的其他特性

(1)重载成员变量

Screen myScrren;
char ch = myScreen.get();
ch = myScreen.get(0,0);

(2)类数据成员的初始化

类内初始值必须使用=或者{}的初始化形式。

class Window_mgr{
private:
    std::vector<Screen> screens{Screen(24, 80, ' ')};
}

(3)基于const的重载

class Screen {
public:
	// display overloaded on whether the object is const or not
    Screen &display(std::ostream &os) 
                  { do_display(os); return *this; }
    const Screen &display(std::ostream &os) const
                  { do_display(os); return *this; }
}

当某个对象调用display的时候,该对象是否是const决定了应该调用display的哪个版本。

(3)类类型

对于一个类来说,在我们创建他的对象之前该类必须被定义过,而不能仅被声明。

(4)友元

友元类

如果一个类指定了友元类,则友元类的成员函数可以访问此类包括非公有成员在内的所有成员。

class Screen {
	// Window_mgr的成员可以访问Screen类的私有部分
	friend class Window_mgr;
}

令成员函数作为友元

class Screen {
	// Window_mgr::clear必须在Screen类之前被声明
	friend void Window_mgr::clear(ScreenIndex);
}

7.3 类的其他特性

(1)重载成员变量

Screen myScrren;
char ch = myScreen.get();
ch = myScreen.get(0,0);

(2)类数据成员的初始化

类内初始值必须使用=或者{}的初始化形式。

class Window_mgr{
private:
    std::vector<Screen> screens{Screen(24, 80, ' ')};
}

(3)基于const的重载

class Screen {
public:
	// display overloaded on whether the object is const or not
    Screen &display(std::ostream &os) 
                  { do_display(os); return *this; }
    const Screen &display(std::ostream &os) const
                  { do_display(os); return *this; }
}

当某个对象调用display的时候,该对象是否是const决定了应该调用display的哪个版本。

(3)类类型

对于一个类来说,在我们创建他的对象之前该类必须被定义过,而不能仅被声明。

(4)友元

友元类

如果一个类指定了友元类,则友元类的成员函数可以访问此类包括非公有成员在内的所有成员。

class Screen {
	// Window_mgr的成员可以访问Screen类的私有部分
	friend class Window_mgr;
}

令成员函数作为友元

class Screen {
	// Window_mgr::clear必须在Screen类之前被声明
	friend void Window_mgr::clear(ScreenIndex);
}

7.4 类的作用域

一个类就是一个作用域。

7.5 构造函数再探

(1)构造函数的初始值有时必不可少

::: tip

如果成员是const、引用,或者属于某种未提供默认构造函数的类类型化。我们必须通过构造函数初始值列表为这些成员提供初值。

:::

class ConstRef{
public:
	ConstRef (int i);
private:
	int i;
	const int ci;
	int &ri;
};

ConstRef:ConstRef(int ii) : i(ii), ci(ii), ri(i){  }

(2)成员初始化的顺序

成员初始化的顺序与它们在类定义中出现 的顺序一致。P259

(3)委托构造函数

使用它所述类的其他构造函数执行它自己的初始化过程。

(4)如果去抑制构造函数定义的隐式转换?

在类内声明构造函数的时候使用explicit关键字。

7.6 类的静态成员

(1)声明静态成员

在成员的声明之前加上关键词static。

类的静态成员存在于任何对象之外,对象中不包含任何与静态成员有关的数据。

(2)使用类的静态成员

double r;
r = Account::rate();

小结

类有两项基本能力:

一是数据数据抽象,即定义数据成员和函数成员的能力;

二是封装,即保护类的成员不被随意访问的能力。