1. 程式人生 > >C++ 類繼承的深入理解(未完待續)

C++ 類繼承的深入理解(未完待續)

說起繼承,就是對現有的類的方法和欄位進行重複的使用

(1)公有繼承父類的方法,繼承完父類所有的屬性,都繼承到了子類,這些欄位都是子類的了,

不存在是父類的,還是子類的欄位,都是子類的。只是子類能否用自己繼承的方法進行訪問而已。 

對於父類的私有成員,雖然子類可以繼承這個方法,但是要依靠父類的方法,進行訪問自己繼承下來的欄位。

上面的這些總結有點饒口,重新梳理下,簡單的解釋如下:在公有繼承的方式下

在父類的訪問屬性 在子類中的訪問屬性
public(公用) public
private(私有) private
protected(保護) 不可訪問

這裡有一點一定要明確,子類繼承了父類的私有成員,包括,私有的資料成員和成員函式,在子類的訪問屬性是不可訪問是

指在子類中的新增成員函式,或者是重寫的成員函式裡,不能夠進行訪問,從父類繼承過來的成員函式,還是可以訪問的。

這個一定要分清,從父類繼承過的函式,是程式碼的公用。                                                                           

class ICommandRunnable :virtual public IRunable
{
public:
    ICommandRunnable();
    virtual ~ICommandRunnable();
public:
    virtual void PushCommand(BaseCommand *cmd, bool front = false);
protected:
    virtual void ClearCommand();
    virtual BaseCommand *PopCommand();
private:
    std::deque<BaseCommand*> _cmds;
    CLock _cmds_lock;

};

像上面這個類,是一個父類,但是它的_cmds欄位,子類繼承以後是訪問不了的,需要藉助繼承到自身子類的父類方法進行訪問。

class TestICommand :public ICommandRunnable
{
public:
TestICommand();
~TestICommand();
//執行緒執行函式
//virtual void  PushCommand(BaseCommand *cmd, bool front = false);
void run();
private:
//執行緒啟動鎖
bool m_bIsRunning;
CLock m_lkRunLock;
SegEvent m_evtWait;

};

void TestICommand::run()
{
while (m_bIsRunning)
{

BaseCommand* pCmd = NULL;              
PushCommand(pCmd);                                 //這裡只是程式碼的構成,並不是真實的寫法,只是為了解釋而進行的程式碼書寫
if ((pCmd = (BaseCommand*)PopCommand()) == NULL)

{
m_evtWait.wait(3000);
continue;
}
}

}

像上面的兩個方法,一個是父類的公有成員函式 PushCommand,一個是父類的受保護成員函式PopCommand

(受保護的成員函式在父類裡相當於私有成員,只是繼承到子類的時候,子類內部可以訪問,該成員,在類外是不能訪問的,這裡在強調下什麼是類內,就是在定義類的時候,類的成員函式可以進行訪問,而外部直接定義一個類物件,然後直接訪問物件的成員屬性是訪問不了的。)

子類繼承了這個方法,然後是可以直接使用的,來訪問 繼承到自身的,父類的私有成員,_cmds,如果你把父類的兩個方法改成private那就不行了,子類雖然繼承過來這個方法,但是不能訪問,像上面那樣呼叫就會報錯了。

而另外還有一種方式,就是你想繼承父類的方法,你感覺你除了要做父類的事以外,你還想要乾點其它的事,也就是說父類寫的方法,pushcommand不能夠滿足你的需求 了,那就可以對父類的方法進行重寫。

如下所示:

 

 

 

class TestICommand :public ICommandRunnable
{
public:
TestICommand();
~TestICommand();
//執行緒執行函式
virtual void  PushCommand(BaseCommand *cmd, bool front = false);
void run();
private:
//執行緒啟動鎖
bool m_bIsRunning;
CLock m_lkRunLock;
SegEvent m_evtWait;

 

};

void  TestICommand::PushCommand(BaseCommand *cmd, bool front = false)
{
//_cmds.push_front(cmd);

ICommandRunnable::PushCommand(cmd);

        m_evtWait.set();

}

我在pushcommand後想讓執行緒跑起來,就得新增一個m_evtWait.set();而原來的父類的函式就滿足不了需求了,

就可以採用兩種方式來處理這個問題

(1)我就要對這個方法進行重寫,也就是多型,然而我會先不呼叫父類父類的方法,我想直接按照紅色部分操作,這是不可以的,因為這個欄位在父類那裡是私有的,子類自己寫的方法是不能進行訪問的,所以要用父類的方法進行訪問,所以應該寫成第二種綠色的形式,進行訪問,然後後面再加上額外的操作。

(2)不用對父類的方法進行重寫,原來繼承下來的方法繼續使用,新定於一個方法,Start.

如下程式碼所示:

class  Threadimpl
{
public:
Threadimpl();
virtual ~Threadimpl();
void  startimpl(IRunable* prunable);
void  joinImpl();
bool  joinImpl(int  milliseconds);
bool  isRunningImpl() const;
static void sleepImpl(int milliseconds);
#if defined(_USRDLL)
typedef DWORD(WINAPI *Entry)(LPVOID);
#else
typedef unsigned(__stdcall *Entry)(void*);
#endif
protected:
void threadCleanup();
//#if defined(_USRDLL)
static DWORD WINAPI runnableEntry(LPVOID pThread);
//#else
// static unsigned  runnableEntry(void* pThread);
//#endif


private:
IRunable* _pRunnableTarget;
int _stackSize;
int _nParam;
HANDLE _thread;
};
inline void Threadimpl::sleepImpl(int milliseconds)
{
Sleep(DWORD(milliseconds));
}


class  Thread : public Threadimpl
{
public:
Thread();
virtual ~Thread();
void  start(IRunable* prunable);//下面的所有函式都是直接呼叫父類的函式
void join();
bool join(int milliseconds);
bool isRunning() const;
static void sleep(int milliseconds);
};

class TestICommand :public ICommandRunnable,public Thread
{
public:
TestICommand();
~TestICommand();
//執行緒執行函式
bool Start();
bool Stop();
void run();
private:
//執行緒啟動鎖
bool m_bIsRunning;
CLock m_lkRunLock;
SegEvent m_evtWait;

 

};

bool TestICommand::Start()
{
if (!m_bIsRunning)
{
m_evtWait.reset();
m_bIsRunning = true;
start(this);
}
return true;
}


bool TestICommand::Stop()
{
if (m_bIsRunning)
{
m_bIsRunning = false;
m_evtWait.set();
join();
}
return true;
}

函式裡呼叫繼承的函式,然後再新增自己想要的處理,這樣也可以滿足需求。這個是針對TestICommand 繼承Thread來舉例說明的