面向对象编程风格 VS 基于对象编程风格

面向对象编程风格 VS 基于对象编程风格

介绍

面向对象的三大特点(封装,继承,多态)缺一不可。

通常“基于对象”是使用对象,但是无法利用现有的对象模板产生新的对象类型,继而产生新的对象,也就是说“基于对象”没有继承的特点。而“多态”表示为父类类型的子类对象实例,没有了继承的概念也就无从谈论“多态”。

现在的很多流行技术都是基于对象的,它们使用一些封装好的对象,调用对象的方法,设置对象的属性。但是它们无法让程序员派生新对象类型。他们只能使用现有对象的方法和属性。

所以当你判断一个新的技术是否是面向对象的时候,通常可以使用后两个特性来加以判断。“面向对象”和“基于对象”都实现了“封装”的概念,但是面向对象实现了“继承和多态”,而“基于对象”没有实现这些。

本文主要通过实现Thread类来展现两种编程风格的不同点。

面向对象风格

OO_PC

Noncopyable.h

#pragma once

namespace wd
{

class Noncopyable
{
protected:
	Noncopyable(){}
	~Noncopyable() {}

	Noncopyable(const Noncopyable &) = delete;
	Noncopyable & operator=(const Noncopyable &) = delete;
};

}//end of namespace wd

Thread.h

#pragma once

#include "Noncopyable.h"
#include <pthread.h>

namespace wd
{

class Thread 
: Noncopyable
{
public:
	Thread()
	: _pthid(0)
	, _isRunning(false)
	{}

	virtual ~Thread();

	void start();
	void join();

private:
	virtual void run() = 0;
	static void * threadFunc(void *);//没有隐含的this 指针

private:
	pthread_t  _pthid;
	bool _isRunning;
};

}//end of namespace wd

Thread.cc

#include "Thread.h"

#include <stdio.h>
#include <errno.h>
#include <iostream>
using std::cout;
using std::endl;

namespace wd
{

Thread::~Thread()
{
	if(_isRunning) {
		//pthread_detach(_pthid);	
		_isRunning = false;
	}
	cout << "~Thread()" << endl;
}

void Thread::start()
{
	if(pthread_create(&_pthid, nullptr, threadFunc, this)) {
		perror("pthread_create");
		return;
	}

	_isRunning = true;
}

void Thread::join()
{
	if(_isRunning) {
		pthread_join(_pthid, nullptr);
		_isRunning = false;
	}
}

void * Thread::threadFunc(void * arg)
{
	Thread * pthread = static_cast<Thread*>(arg);
	if(pthread)
		pthread->run();

	return nullptr;
}

}//end of namespace wd

TestThread.cc

#include "Noncopyable.h"
#include "Thread.h"

#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <iostream>
#include <memory>
using namespace std;


class Mythread
: public wd::Thread
{
public:
	~Mythread() {	cout << "~Mythread()" << endl;}

private:
	void run() override
	{
		::srand(::clock());
		int cnt = 10;
		while(cnt--) {
			int number = ::rand() % 100;
			cout << "sub thread " << pthread_self() 
				 << ": get a number = " << number << endl;
			::sleep(1);
		}
	}

};
             
 
int main(void)
{
	cout << "main thread " << pthread_self() << endl;

	unique_ptr<wd::Thread> mythread(new Mythread());
	mythread->start();
	mythread->join();

	return 0;
}

Tips

  1. Thread类是虚基类,MyThread类通过继承实现虚函数run()。
  2. 我们先看pthread_create的原型:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);

start_routine的形式是函数指针,故不能直接将run()作为参数,因为run()是成员函数,隐含this指针,故实现一个静态成员函数static void * threadFunc(void * arg),在里面调用run(),此外参数arg我们传递this指针,在派生类中将指针转换为基类指针来调用run()

  1. 把run()实现为private,目的为了不让用户直接调用,否则违背了线程的初衷。

基于对象风格

BO_PC

Thread.cc

#pragma once

#include "Noncopyable.h"
#include <pthread.h>
#include <functional>

namespace wd
{

class Thread 
: Noncopyable
{
public:
	using ThreadCallback = std::function<void()>;

	Thread(ThreadCallback && cb)
	: _pthid(0)
	, _isRunning(false)
	, _cb(std::move(cb))
	{}

	~Thread();

	void start();
	void join();

private:
	static void * threadFunc(void *);

private:
	pthread_t  _pthid;
	bool _isRunning;
	ThreadCallback _cb;
};

}//end of namespace wd

TestThread.cc

#include "Thread.h"

#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <iostream>
#include <memory>
using namespace std;


class MyTask 
{
public:
	~MyTask() {	cout << "~MyTask()" << endl;}

	void process() 
	{
		::srand(::clock());
		int cnt = 10;
		while(cnt--) {
			int number = ::rand() % 100;
			cout << "sub thread " << pthread_self() 
				 << ": get a number = " << number << endl;

			::sleep(1);
		}
	}
};
             
 
int main(void)
{

	cout << ">> main thread " << pthread_self() << endl;

	MyTask task;

	//如果是以指针的方式传递对象,要保证在执行process方法时,
	//该对象生命周期没有结束;如果结束,执行会出错
	//
	//绑定对象时,也可以以值传递方式传递对象(多析构一次)
	unique_ptr<wd::Thread> mythread(
		new wd::Thread(std::bind(&MyTask::process, &task)));
	mythread->start();
	mythread->join();

	return 0;
}

Tips

bind/function 实现转换函数接口。

  1. 如果绑定的是一般的函数,则bind 中的参数中不再需要this指针,当然一般函数也没有类名前缀。
  2. 如果绑定的成员函数,则bind中的参数需要this指针,而且需要加上类名和取地址符号。
  3. Thread类不再是虚基类,run() 也不是虚函数,Thread 有个成员threadFunc,此时不再是通过继承基类来重新实现run(),进而实现多态;而是通过绑定不同的函数指针到threadFunc上来实现不同的行为。
  4. 我们既可以绑定一般的全局函数,也可以绑定其他类里面的成员函数,操作很方便。

小结

一个object-based设计可能比一个对等的objectoriented设计速度更快而且空间更紧凑,速度快是因为所有的函数引发操作都在编译时期解析完成,对象构建起来时不需要设置virtual机制;空间紧凑是因为每一个class object不需要负担传统上为了支持virtual机制而需要的额外负荷(virtual table),不过OB设计比较没有弹性。


基于对象的语言

  • 基于对象的语言支持对象和封装的使用。

  • 它们不支持继承或多态或两者

  • 基于对象的语言不支持内置对象。

  • Javascript,VB是对象基础语言的示例。

面向对象的语言

  • 面向对象的语言支持Oops的所有功能,包括继承和多态。
  • 它们支持内置对象。
  • C#,Java,VB。Net是面向对象语言的示例。

参考:

  1. https://www.tutorialspoint.com/what-is-the-difference-between-object-oriented-programming-and-object-based-programming
  2. https://blog.csdn.net/daaikuaichuan/article/details/85945208
  3. 《深度探索C++对象模型》
posted @ 2019-06-11 21:47  Mered1th  阅读(303)  评论(0编辑  收藏  举报