5-面向对象编程风格

Posted itzyjr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了5-面向对象编程风格相关的知识,希望对你有一定的参考价值。

5.1面向对象编程概念(Object-Oriented Programming Concepts)

面向对象编程概念的两项最主要特质是:继承(inheritance)多态(polymorphism)

继承:
使我们得以将一群相关的类组织起来,并让我们得以分享其间的共通数据和操作行为。
继承机制定义了父子(parent/child)关系。父类(parent)定义了所有子类(children)共通的公有接口(public interface)和私有实现(private implementation)。每个子类都可以增加或覆盖(override)继承而来的东西,以实现其自身独特的行为。
在C++中,父类被称为基类(base class),子类被称为派生类(derived class)。父类和子类之间的关系则称为继承体系(inheritance hierarchy)。

多态:
让我们在这些类之上进行编程时,可以如同操控单一个体,而非相互独立的类,并赋予我们更多弹性来加入或移除任何特定类。让基类的pointer或reference得以十分透明地(transparently)指向其任何一个派生类的对象。

抽象基类(abstract base class):LibMat。
LibMat用来定义图书馆借阅管理系统中所有馆藏的共通操作行为,包括:check_in()、check_out()、due_date()、find()、location(),等等。
LibMat并不代表图书馆借阅管理系统中实际存在的任何一个馆藏,仅仅是为了我们设计上的需要而存在。但事实上这个抽象十分关键。我们称之为“抽象基类”。

LibMat
Book
ChildToys
Manazines
Films
RentalBook
AudioBook
CDIBook
             “图书馆借阅系统”中的“外借馆藏类继承体系”
void loan_check_in(LibMat &mat) {
	// mat实际上代表某个派生类的对象(derived clas object)
	// 诸如Book、RetalBook、Magazines,等等
	mat.check_in();
	if (mat.is_late())
		mat.assess_fine();
	if (mat.waiting_list())
		mat.notify_available();
}

以上述的loan_check_in()为例,mat总是指向(代表)LibMat的某个派生对象。但究竟是哪一个?答案是除非程序实际执行的当下,否则无法确实。而且,loan_check_in()的每次执行情况可能不同。
每次loan_check_in()执行时,仅能在执行过程中依据mat所指的实际对象来决定调用哪一个check_in()。

5.2漫游:面向对象编程思维(A Tour of Object-Oriented Programming)

默认情形下,member function的解析(resolution)皆在编译时静态地进行。若要令其在运行时动态进行,我们就得在它的声明前加上virtual。LibMat的声明表示,其destructor和print()皆为virtual(虚函数)。

class LibMat {
public:
	LibMat() {
		cout << "LibMat::LibMat() default constructor!\\n";
	}
	virtual ~LibMat() {
		cout << "LibMat::~LibMat() destructor!\\n";
	}
	virtual void print() const {
		cout << "LibMat::print() -- I am a LibMat object!\\n";
	}
};

现在,我们定义一个非成员函数print(),它接受一个形式为const LibMat reference的参数:

void print(const LibMat &mat) {
	cout << "in global print(): about to print mat.print()\\n";
	// 下一行会依据mat实际指向的对象,
	// 解析该执行哪一个print() member function。
	mat.print();
}

第一次调用操作像下面这样:

LibMat libmat;
print(libmat);

以下是跟踪结果:

// 构造LibMat libmat
LibMat::LibMat() // 默认构造函数
// 处理print(libmat)
in global print(): about to print mat.print()
LibMat::print() -- I am a LibMat object!
// 析构Libmat libmat
LibMat::~LibMat() // 析构函数

当我们将Book对象传入print()时:

Book b("The Castle", "Franz Kafka");
print(b);

以下是跟踪结果:

// 构造Book b
LibMat::LibMat() // A1--默认构造函数
Book::Book(The Castle, Franz Kafka) // A2--构造函数
// 处理print(b)
in global print(): about to print mat.print()
Book::print() -- I am a Book object!
My title is: The Castle
My author is: Franz Kafka
// 析构Book b
Book::~Book() // B1--板构函数
LibMat::~LibMat() // B2--析构函数

在main()程序中重复调用print(),并依次将LibMat对象、Book对象、AudioBook对象当作参数传递给它。每次print()被执行,都会依据mat实际所指的对象,在LibMat、Book、AudioBook三者之间挑选正确的print()成员函数加以调用。
被调用的函数是Book::print()而非LibMat::print()。
当程序定义出一个派生对象,基类和派生类的构造函数都会被执行。当派生对象被销毁,基类和派生类的析构函数也都会被执行,但次序颠倒。

如何实现派生类Book呢?

class Book : public LibMat {
public:
	Book(const string &title, const string &author): _title(title), _author(author) {
		cout << "Book::Book( " << _title << ", " << _author << " ) constructor\\n";
	}
	virtual ~Book() {
		cout << "Book::~Book() destructor!\\n";
	}
	virtual void print() const {
		cout << "Book::print() -- I am a Book object!\\n"
			 << "My title is: " << _title << '\\n'
			 << "My author is: " << _author << endl;
	}
	const string& title() const { return _title; }
	const string& author() const { return _author; }
protected:
	string _title;
	string _author;
};

Book中的print()覆盖(override)了LibMat的print()。这也正是mat.print()所调用的函数。title()和author()是两个所谓的访问函数(access function),都是non-virtual inline函数。被声明为protected的所有成员都可以被派生类直接访问,除派生类之外,都不得访问protected成员。

如何实现AudioBook这个派生类呢?

class AudioBook : public Book {
public:
	AudioBook(const string &title, const string &author, const string &narrator)
	: Book(title, author), _narrator(narrator) {
		cout << "AudioBook::AudioBook( " << _title
			 << ", " << _author
			 << ", " << _narrator
			 << " ) constructor\\n";
	}
	~AudioBook() {
		cout << "AudioBook::~AudioBook() destructor!\\n";
	}
	virtual void print() const {
		cout << "AudioBook::print() -- I am an AudioBook object!\\n"
		// 注意,以下直接访问继承而来的data member: _title和_author
			 << "My title is: " << _title << '\\n'
			 << "My author is: " << _author << '\\n'
			 << "My narrator is: " << _narrator << endl;
	}
	const string& narrator() const { return _narrator; }
protected:
	string _narrator;
};

合并在一起,一个能够编译运行通过的完整例子:

#include <iostream>
using namespace std;
class LibMat {
public:
	LibMat() {
		cout << "LibMat::LibMat() default constructor!\\n";
	}
	virtual ~LibMat() {
		cout << "LibMat::~LibMat() destructor!\\n";
	}
	virtual void print() const {
		cout << "LibMat::print() -- I am a LibMat object!\\n";
	}
};

void print(const LibMat& mat) {// 非成员方法
	cout << "in global print(): about to print mat.print()\\n";
	mat.print();
}

class Book : public LibMat {
public:
	Book(const string& title, const string& author) : _title(title), _author(author) {
		cout << "Book::Book( " << _title << ", " << _author << " ) constructor\\n";
	}
	virtual ~Book() {
		cout << "Book::~Book() destructor!\\n";
	}
	virtual void print() const {
		cout << "Book::print() -- I am a Book object!\\n"
			<< "My title is: " << _title << '\\n'
			<< "My author is: " << _author << endl;
	}
	const string& title() const {
		return _title;
	}
	const string& author() const {
		return _author;
	}
protected:
	string _title;
	string _author;
};

class AudioBook : public Book {
public:
	AudioBook(const string& title, const string& author, const string& narrator)
		: Book(title, author), _narrator(narrator) {
		cout << "AudioBook::AudioBook( " << _title
			<< ", " << _author
			<< ", " << _narrator
			<< " ) constructor\\n";
	}
	~AudioBook() {
		cout << "AudioBook::~AudioBook() destructor!\\n";
	}
	virtual void print() const {
		cout << "AudioBook::print() -- I am an AudioBook object!\\n"
			// 注意,以下直接访问继承而来的data member: _title和_author
			<< "My title is: " << _title << '\\n'
			<< "My author is: " << _author << '\\n'
			<< "My narrator is: " << _narrator << endl;
	}
	const string& narrator() const {
		return _narrator;
	}
protected:
	string _narrator;
};

int main() {
	LibMat libmat;
	print(libmat);

	Book book("X", "Y");
	print(book);

	AudioBook audiobook("a", "b", "c");
	print(audiobook);
}
LibMat::LibMat() default constructor!
in global print(): about to print mat.print()
LibMat::print() -- I am a LibMat object!
LibMat::LibMat() default constructor!
————————————————————————
Book::Book( X, Y ) constructor
in global print(): about to print mat.print()
Book::print() -- I am a Book object!
My title is: X
My author is: Y
LibMat::LibMat() default constructor!
Book::Book( a, b ) constructor
————————————————————————
AudioBook::AudioBook( a, b, c ) constructor
in global print(): about to print mat.print()
AudioBook::print() -- I am an AudioBook object!
My title is: a
My author is: b
My narrator is: c
AudioBook::~AudioBook() destructor!
Book::~Book() destructor!
LibMat::~LibMat() destructor!
Book::~Book() destructor!
LibMat::~LibMat() destructor!
LibMat::~LibMat() destructor!

从以上可以看到,destructor是最后次序颠倒依次执行的。并且不像JAVA,JAVA的是abstract class,里面的方法可以是abstract的,也可以直接定义,abstract class不能直接被实例化,所以抽象方法不可能被调用。class才可实例化并调用方法。如果不继承实现抽象方法,是不可以实例化的。而C++中虚函数可以直接定义,也是可以直接调用的。

###对于【JAVA】###

// A.java
public abstract class A {
	abstract int go();
	int yes() {
		return 1;
	}
}
——————————————————————————
// B.java
public class B extends A {
	@Override
	int go() {
		return 0;
	}
}
// T.java
public class T {
	public static void main(String[] args) {
		A a = new A();// 报错:Cannot instantiate the type A
		B b = new B();// 正确
		b.go();
	}
}

###对于【C++】###
去掉所有virtual关键字,结果是一样的。

使用派生类时不必刻意区分“继承而来的成员”和“自身定义的成员”,两者的使用完全透明。

5.3不带继承的多态(Polymorphism without Inheritance)

上节中的num_sequence class模拟了多态的行为。

for (int i = 1; i < num_sequence::num_of_sequences(); ++i) {
	ns.set_sequence(num_sequence::nstype(i));
	int以上是关于5-面向对象编程风格的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段9——JS中的面向对象编程

5种主要的编程风格和它们使用的抽象

原来我们一直写的是违反面向对象编程风格的代码

原来我们一直写的是违反面向对象编程风格的代码

Python 面向对象和实例属性

三种违反面向对象编程风格的典型代码设计