C++仅仅提供了实现继承
意味着所有的内容总是继承自基类
好处就是 程序员不得不在派生类中实现所有的细节
多重继承的一个共同用途使用 混入类 mixin
假设一个客户使用了某个类
该类支持访问一个数据库
仅仅只有一个头文件使用
客户不能访问实现具体功能的源代码
//: C09:Database.h
// From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison.
// (c) 1995-2004 MindView, Inc. All Rights Reserved.
// See source code use permissions stated in the file 'License.txt',
// distributed with the code package available at www.MindView.net.
// A prototypical resource class.
#ifndef DATABASE_H
#define DATABASE_H
#include <iostream>
#include <stdexcept>
#include <string>
struct DatabaseError : std::runtime_error {
DatabaseError(const std::string& msg)
: std::runtime_error(msg) {}
};
class Database {
std::string dbid;
public:
Database(const std::string& dbStr) : dbid(dbStr) {}
virtual ~Database() {}
void open() throw(DatabaseError) {
std::cout << "Connected to " << dbid << std::endl;
}
void close() {
std::cout << dbid << " closed" << std::endl;
}
// Other database functions...
};
#endif // DATABASE_H ///:~
这里省略了实际的数据库功能
使用类需要一个数据库连接串
调用Database::open()来连接数据库
通过调用Database::close()断开连接
//: C09:UseDatabase.cpp
// From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison.
// (c) 1995-2004 MindView, Inc. All Rights Reserved.
// See source code use permissions stated in the file 'License.txt',
// distributed with the code package available at www.MindView.net.
#include "Database.h"
int main() {
Database db("MyDatabase");
db.open();
// Use other db functions...
db.close();
getchar();
}
/* Output:
connected to MyDatabase
MyDatabase closed
*/ ///:~
输出
Connected to MyDatabase
MyDatabase closed
在典型的客户机-服务器模式的情况下
客户拥有多个对象
对象分享一个连接的数据库
尽管数据库的最后关闭非常重要
当数据库只能在不需要访问它之后关闭
给Database引入技术
利用多重继承将一个叫Countable的类混入Database类中
创建一个新类DBConnection
//: C09:Countable.h
// From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison.
// (c) 1995-2004 MindView, Inc. All Rights Reserved.
// See source code use permissions stated in the file 'License.txt',
// distributed with the code package available at www.MindView.net.
// A "mixin" class.
#ifndef COUNTABLE_H
#define COUNTABLE_H
#include <cassert>
class Countable {
long count;
protected:
Countable() { count = 0; }
virtual ~Countable() { assert(count == 0); }
public:
long attach() { return ++count; }
long detach() {
return (--count > 0) ? count : (delete this, 0);
}
long refCount() const { return count; }
};
#endif // COUNTABLE_H ///:~
这不是一个独立类
因为它的构造函数是protected类型
需要一个友元或派生类来使用它
DBConnection类继承了Database和Countable
并且提供了一个静态的create()函数
//: C09:DBConnection.h
// From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison.
// (c) 1995-2004 MindView, Inc. All Rights Reserved.
// See source code use permissions stated in the file 'License.txt',
// distributed with the code package available at www.MindView.net.
// Uses a "mixin" class.
#ifndef DBCONNECTION_H
#define DBCONNECTION_H
#include <cassert>
#include <string>
#include "Countable.h"
#include "Database.h"
using std::string;
class DBConnection : public Database, public Countable {
DBConnection(const DBConnection&); // Disallow copy
DBConnection& operator=(const DBConnection&);
protected:
DBConnection(const string& dbStr) throw(DatabaseError)
: Database(dbStr) { open(); }
~DBConnection() { close(); }
public:
static DBConnection*
create(const string& dbStr) throw(DatabaseError) {
DBConnection* con = new DBConnection(dbStr);
con->attach();
assert(con->refCount() == 1);
return con;
}
// Other added functionality as desired...
};
#endif // DBCONNECTION_H ///:~
不用修改Database类
现在有一个引用计数的数据库连接
并且可以确保数据库连接不会被偷偷终止
通过DBConnection的构造函数和析构函数
使用资源式初始化 the Resource Acquisition Is Initialization
方法来实现数据库的打开和关闭
//: C09:UseDatabase2.cpp
// From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison.
// (c) 1995-2004 MindView, Inc. All Rights Reserved.
// See source code use permissions stated in the file 'License.txt',
// distributed with the code package available at www.MindView.net.
// Tests the Countable "mixin" class.
#include <cassert>
#include "DBConnection.h"
class DBClient {
DBConnection* db;
public:
DBClient(DBConnection* dbCon) {
db = dbCon;
db->attach();
}
~DBClient() { db->detach(); }
// Other database requests using db...
};
int main() {
DBConnection* db = DBConnection::create("MyDatabase");
assert(db->refCount() == 1);
DBClient c1(db);
assert(db->refCount() == 2);
DBClient c2(db);
assert(db->refCount() == 3);
// Use database, then release attach from original create
db->detach();
assert(db->refCount() == 2);
getchar();
} ///:~
输出
Connected to MyDatabase
因为对DBConnection::create()的调用有调用了attach()
在结束时
必须显式调用detach()来释放数据库的初始连接
模板方法一般用于混入继承
允许用户在编译时指定想要的混入类的类型
可以使用不同的引用计数方法来完成这项工作
不用显式地两次定义DBConnection
//: C09:DBConnection2.h
// From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison.
// (c) 1995-2004 MindView, Inc. All Rights Reserved.
// See source code use permissions stated in the file 'License.txt',
// distributed with the code package available at www.MindView.net.
// A parameterized mixin.
#ifndef DBCONNECTION2_H
#define DBCONNECTION2_H
#include <cassert>
#include <string>
#include "Database.h"
using std::string;
template<class Counter>
class DBConnection : public Database, public Counter {
DBConnection(const DBConnection&); // Disallow copy
DBConnection& operator=(const DBConnection&);
protected:
DBConnection(const string& dbStr) throw(DatabaseError)
: Database(dbStr) { open(); }
~DBConnection() { close(); }
public:
static DBConnection* create(const string& dbStr)
throw(DatabaseError) {
DBConnection* con = new DBConnection(dbStr);
con->attach();
assert(con->refCount() == 1);
return con;
}
// Other added functionality as desired...
};
#endif // DBCONNECTION2_H ///:~
变化是用于类定义的模板前缀
可以把某个数据库类作为一个模板参数
但它并不是一个混入类
//: C09:UseDatabase3.cpp
// From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison.
// (c) 1995-2004 MindView, Inc. All Rights Reserved.
// See source code use permissions stated in the file 'License.txt',
// distributed with the code package available at www.MindView.net.
// Tests a parameterized "mixin" class.
#include <cassert>
#include "Countable.h"
#include "DBConnection2.h"
class DBClient {
DBConnection<Countable>* db;
public:
DBClient(DBConnection<Countable>* dbCon) {
db = dbCon;
db->attach();
}
~DBClient() { db->detach(); }
};
int main() {
DBConnection<Countable>* db =
DBConnection<Countable>::create("MyDatabase");
assert(db->refCount() == 1);
DBClient c1(db);
assert(db->refCount() == 2);
DBClient c2(db);
assert(db->refCount() == 3);
db->detach();
assert(db->refCount() == 2);
getchar();
} ///:~
输出 没有错误对话框
Connected to MyDatabase