青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品



看到這道題,我們就開始設計這個圖像類了,按照面向對象“依賴倒置”的設計原則,我們站在客戶的立場,來考慮我們這個類該提供哪些接口,很快我們設計了如下一個類:

class CSimplePicture
{
public:
    CSimplePicture(
char* init[], int nCount); 
    CSimplePicture(CSimplePicture
& p1, CSimplePicture& p2, bool bVerCat);

    
void Frame();
    
void Print(std::ostream& os) const;
protected:
    std::vector
<std::string> m_arData;
};

CSimplePicture(char* init[], int nCount);
根據字符串數組構造一幅圖像.

CSimplePicture(CSimplePicture& p1, CSimplePicture& p2, bool bVerCat);
根據兩幅圖像構造一幅圖像,bVerCat表明是縱聯接還是橫聯接.

void Frame();
給圖像對象加框

void Print(std::ostream& os) const;
打印輸出圖像

std::vector<std::string> m_arData;
存儲圖像數據的字符串數組

下面來考慮具體實現,這個對于有一定開發的經驗的人來說還是很容易的,就不具體寫了,
CSimplePicture(char* init[], int nCount)無非是數據的拷貝,CSimplePicture(CSimplePicture& p1, CSimplePicture& p2, bool bVerCat)就是把2幅圖片的數據連接,合在一起,void Frame()修改里面的數據加上邊框,void Print(std::ostream& os) const遍歷字符串數組輸出。


根據上面的設計和實現,應該已經滿足我們這個題目的要求了。
但是客戶的需求是多變的,現在客戶又有一個新的需求,要求把一幅圖片去掉邊框。
另外客戶覺得我們這個圖片類的性能太差了,每次加框或是合成圖片都要大量的內存拷貝。

這時我們傻眼了,該死的客戶,根據我們上面的設計,根本不支持這些新功能,因為我們存儲的是圖像的內部的字符串數據,根本不知道它是不是加框過的,另外我們的圖像數據本身就是不支持共享的。

 

接下來我們就要重新考慮設計了,如何讓我們的圖像對象支持UnFrame(去邊框)操作,關鍵是要建立我們的圖像類型層次,這樣就可以判斷是否是加框的類對象,于是有了如下的類層次:
//圖象接口基類
class CPic_Base
{};

//字符串圖像類
class CPic_String: public CPic_Base
{};

//加框圖像類
class CPic_Frame: public CPic_Base
{}

//縱聯接圖像類
class CPic_VCat: public CPic_Base
{};

//橫聯接圖像類
class CPic_HCat: public CPic_Base
{};

然后我們考慮如何共享圖像數據,這就要用到智能指針了,智能指針在C++里一般有2種實現,一種是STL 里的auto_ptr,還有一種就是基于引用計數。auto_ptr的本質是擁有關系,也就是你擁有了這對象后,別人就不能擁有了,所以這里不符合我們的要求。引用計數是個好東西,對于共享對象特別有用,COM里的IUnknow接口就是基于這個技術的,還有很多腳本語言里變量自動銷毀,實際上都是基于引用計數的技術。這里分享一個基于引用計數的智能指針類。

class CRefCountBase
{
public:
    CRefCountBase()
    {
        m_nRefCount 
= 0;
    }

    
int GetRefCount() const
    {
        
return m_nRefCount;
    }

    
int AddRefCount()
    {
        
return ++m_nRefCount;
    }

    
int SubRefCount()
    {
        
return --m_nRefCount;
    }

    
void ResetRefCount()
    {
        m_nRefCount 
= 0;
    }

private:
    
int    m_nRefCount;
};

template
<typename T>
class CRefPtr
{
public:
    T
* operator->() const
    {
        
return m_pRawObj;
    }

    T
& operator()() const
    {
        
return *m_pRawObj;
    }

    T
& operator*() const
    {
        
return *m_pRawObj;
    }

    T
* GetPtr() const
    {
        
return m_pRawObj;
    }

    
bool IsNull() const
    {
        
return m_pRawObj == NULL;
    }

    CRefPtr()
    {
        m_pRawObj 
= NULL;
    }

    CRefPtr(T
* p)
    {
        m_pRawObj 
= p;
        
if(p != NULL)
        {
            p
->AddRefCount();
        }
    }

    CRefPtr(
const CRefPtr& ref)
    {
        m_pRawObj 
= ref.m_pRawObj;
        
if(m_pRawObj != NULL)
        {
            m_pRawObj
->AddRefCount();
        }
    }

    
~CRefPtr()
    {
        
if(m_pRawObj != NULL && m_pRawObj->SubRefCount() == 0)
        {
            delete m_pRawObj;
        }
    }

    CRefPtr
& operator = (const CRefPtr& ref)
    {
        
if(this != &ref)
        {
            
if(m_pRawObj != NULL
                
&& m_pRawObj->SubRefCount() == 0)
            {
                delete m_pRawObj;
            }

            m_pRawObj 
= ref.m_pRawObj;

            
if(m_pRawObj != NULL)
            {
                m_pRawObj
->AddRefCount();
            }
        }

        
return *this;
    }

    
bool operator == (const CRefPtr& refconst
    {
        
return m_pRawObj == ref.m_pRawObj;
    }

    CRefPtr
<T> Copy()
    {
        
if(m_pRawObj != NULL)
        {
            T
* p = new T(*m_pRawObj);
            p
->ResetRefCount();

            
return p;
        }
        
else
        {
            
return NULL;
        }
    }

private:
    T
* m_pRawObj;
};


這樣使用這個類:

class A: public CRefCountBase
{
Public:
    Void fun1();
};

CRefPtr
<A> p = new A;
p
->fun1();


重新設計我們的CPic_Base,

class CPic_Base: public CRefCountBase
{
public:
    
virtual ~CPic_Base() {}

    
//打印輸出圖像
    void Print(std::ostream& os) const;

    
//返回圖像寬度
    virtual int GetWidth() const = 0;

    
//返回圖像高度
    virtual int GetHeight() const = 0;

    
//返回某行的圖像字符串數據
    virtual std::string GetLineData(int nLineIndex) const = 0;

    
//返回去掉邊框的對象
    virtual CRefPtr<CPic_Base> GetUnFrame() const { return NULL; }
};


這里Print方法實現就很簡單了:

void CPic_Base::Print(std::ostream& os) const
{
    
for(int i=0; i<GetHeight(); ++i)
    {
        os 
<< GetLineData(i);
        os 
<< "\n";
    } 
}


然后考慮實現CPic_String

class CPic_String: public CPic_Base
{
public:
    CPic_String(
char* p[], int nCount);

    
virtual int GetWidth() const;
    
virtual int GetHeight() const;
    
virtual std::string GetLineData(int nLineIndex) const;


protected:
    std::vector
<std::string> m_arData;
};


這個類里存儲真正的字符串圖像數據,里面方法的實現也很簡單,和最開始的的第一種實現類似,就不詳寫了。

再考慮實現CPic_Frame

class CPic_Frame: public CPic_Base
{
public:
    CPic_Frame(CRefPtr
<CPic_Base>& pic);

    
virtual int GetWidth() const;
    
virtual int GetHeight() const;
    
virtual std::string GetLineData(int nLineIndex) const

    
virtual CRefPtr<CPic_Base> GetUnFrame() const { return m_pic; }

protected:
    CRefPtr
<CPic_Base> m_pic;
};


可以看到這里我們引用了一個其他的圖像數據,而不是真正存儲這些數據,方法實現也很簡單, 主要依賴于m_pic所指向的圖像類,同時m_pic是個基于引用計數的智能指針, 所以賦值時也沒有內存拷貝, 注意GetUnFrame這個方法只有這里返回非NULL,表示只有這種對象支持去邊框。

CPic_Frame::CPic_Frame(CRefPtr<CPic_Base>& pic)
: m_pic(pic)
{
    _ASSERTE(
!m_pic.IsNull());
}

int CPic_Frame::GetWidth() const
{
    
return m_pic->GetWidth() + 2;
}

int CPic_Frame::GetHeight() const
{
    
return m_pic->GetHeight() + 2;
}

string CPic_Frame::GetLineData(int nLineIndex) const
{
    
int nWidth = GetWidth();
    
int nHeight = GetHeight();

    _ASSERTE(nLineIndex 
< nHeight && nLineIndex >= 0); 

    
if(nLineIndex == 0 //first line and last line
        || nLineIndex == nHeight - 1)
    {
        
int nPadding = nWidth - 2;
        
return string("+"+ string(nPadding, '-'+ string("+");
    }
    
else
    {
        
return string("|"+ m_pic->GetLineData(nLineIndex - 1+ string("|");
    }
}

再考慮實現CPic_VCat

class CPic_VCat: public CPic_Base
{
public:
    CPic_VCat(CRefPtr
<CPic_Base>& pic1, CRefPtr<CPic_Base>& pic2);

    
virtual int GetWidth() const;
    
virtual int GetHeight() const;
    
virtual std::string GetLineData(int nLineIndex) const;

protected:
    CRefPtr
<CPic_Base> m_pic1;
    CRefPtr
<CPic_Base> m_pic2;
};

他里面存儲了上下2個圖像對象,方法實現是也不復雜,就不具體寫了。

另外CPic_HCat也是類似:

class CPic_HCat: public CPic_Base
{
public:
    CPic_HCat(CRefPtr
<CPic_Base>& pic1, CRefPtr<CPic_Base>& pic2);

    
virtual int GetWidth() const;
    
virtual int GetHeight() const;
    
virtual std::string GetLineData(int nLineIndex) const;

protected:
    CRefPtr
<CPic_Base> m_pic1;
    CRefPtr
<CPic_Base> m_pic2;
};


有了上面的實現,現在我們可以這么實現我們需要的功能了:

Int main()
{
    
char* init1[] = {"Paris""in the""Spring"};
    CRefPtr
<CPic_Base> p1 = new CPic_String(init, 3); 

    CRefPtr
<CPic_Base> p2 = new CPic_Frame(p1);

    CRefPtr
<CPic_Base> p3 = new CPic_VCat(p1, p2);

    P3
->Print(cout);
    CRefPtr
<CPic_Base> p4 = p2->GetUnFrame();
}


這時我們發現這樣對于客戶調用很不友好,因為我們內部實現的類層次都暴露給客戶了,而這些信息對客戶來說應該都是透明的,我們應該再封裝一個更簡單的界面類給客戶。

于是有了如下的設計,其實接口類似我們的第一種實現。

class CPicture
{
public:
    CPicture(
char* p[], int nCount);
    CPicture(CPicture
& p1, CPicture& p2, bool bVerCat);

    
void Frame();
    
bool UnFrame();

    friend std::ostream
& operator << (std::ostream& os, const CPicture& pic);

protected:
    CRefPtr
<CPic_Base> m_pic;
};

std::ostream
& operator << (std::ostream& os, const CPicture& pic);


這樣對客戶來說他們只需要和CPicture打交道,根本不用關心內部的實現。
這個類的實現也很簡單:

CPicture::CPicture(char* p[], int nCount)
{
    m_pic 
= new CPic_String(p, nCount);
}

CPicture::CPicture(CPicture
& pic1, CPicture& pic2, bool bVerCat)
{
    
if(!bVerCat)
    {
        m_pic 
= new CPic_HCat(pic1.m_pic, pic2.m_pic);
    }
    
else
    {
        m_pic 
= new CPic_VCat(pic1.m_pic, pic2.m_pic);
    }
}

void CPicture::Frame()
{
    m_pic 
= new CPic_Frame(m_pic);
}

bool CPicture::UnFrame()
{
    CRefPtr
<CPic_Base> p = m_pic->GetUnFrame();
    
if(!p.IsNull())
    {
        m_pic 
= p;
    }

    
return !p.IsNull();
}

std::ostream
& operator << (std::ostream& os, const CPicture& pic)
{
    pic.m_pic
->Print(os);
    
return os;
}


下面是我們使用這個類的代碼:

char* init1[] = {"Paris""in the""Spring"};
char* init2[] = {"Hello world""every""thing""is""OK!"};

int main(int argc, char* argv[])
{
    CPicture p1(init1, 
3);
    CPicture p2(init2, 
5);

    
//
    std::cout << p1;
    cout 
<<endl << endl; 

    
//
    std::cout << p2;
    cout 
<<endl << endl; 

    
//
    p2.Frame();
    cout 
<< p2;
    cout 
<<endl << endl; 

    
//
    p1.Frame();
    p1.Frame();
    cout 
<< p1;
    cout 
<<endl << endl;

    
//
    CPicture pHorCat(p1, p2, false);
    cout 
<< pHorCat;
    cout 
<<endl << endl; 

    
//
    CPicture pVerCat(p1, pHorCat, true);
    cout 
<< pVerCat;
    cout 
<<endl << endl; 

    
//
    pVerCat.Frame();
    cout 
<< pVerCat;
    cout 
<<endl << endl; 

    
//
    pVerCat.Frame();
    cout 
<< pVerCat;
    cout 
<<endl << endl; 

    
//
    pVerCat.UnFrame();
    pVerCat.UnFrame();
    cout 
<< pVerCat;
    cout 
<<endl << endl; 

    system(
"pause");

    
return 0;
}


可以看到使用起來非常方便和友好,運行截圖:


可以看到使用第二種實現我們只存儲了一份字符串圖像數據,同時有保留了圖像的層次和結構屬性,實現時包含了很多設計模式,比如Template, Decorate, Composite, Facade等,簡單而高效。


最后我們對這2種實現方式作下比較:

方法1的優勢是數據完整,修改一個對象時不會影響其他對象,因為每個對象都是數據的單獨拷貝。劣勢是低效,不能體現對象的結構屬性,我們不知道這個對象是加邊框的對象還是上下合成的對象。


方法2的優勢是高效,數據共享,同時有保留有對象的結構屬性。劣勢是修改一個對像時會影響其他的對象,因為他們可能是共享同一個對象。實際上,對于基于引用計數的共享對象,還有一種叫做Write Copy(寫入時拷貝)的技術,就是如果你要修改一個對象,就自己拷貝一份。同時引用計數技術還有一個風險就是循環引用,比如A引用了B,B也引用了A,這2個對象就永遠沒法釋放了,這也是要謹慎的。

 

上面完美的解決了我們UnFrame(去邊框)的問題,我們正對我們使用基于引用計數的技術來完美的構造字符串圖像類層次而洋洋得意,但是好景不長。


一個星期后,客戶又找到你提了他的新需求,他想讓你的CPicuture類增加一個功能,能返回一個XML格式的字符串來告訴他該對象的構造過程。
比如
+-------+
|Paris   |
|in the |
|Spring |
+-------+
返回的XML串是
< CPic_Frame >
<CPic_String> Paris in the Spring </CPic_String>
</ CPic_Frame >

+-------+Paris
|Paris  |in the
|in the |Spring
|Spring |
+-------+

返回的XML串是:

< CPic_HCat >
< CPic_Frame >
<CPic_String> Paris in the Spring </CPic_String>
</ CPic_Frame >
<CPic_String> Paris in the Spring </CPic_String>
</ CPic_HCat >


+-------+Paris
|Paris  |in the
|in the |Spring
|Spring |
+-------+
Paris
in the
Spring

返回的XML串是:

<CPic_VCat>
< CPic_HCat >
< CPic_Frame >
<CPic_String> Paris in the Spring </CPic_String>
</ CPic_Frame >
<CPic_String> Paris in the Spring </CPic_String>
</ CPic_HCat >
<CPic_String> Paris in the Spring </CPic_String>
</CPic_VCat>

 

你不禁抱怨道,該死的客戶,上次已經因為要支持UnFrame功能而讓我改變了最初的設計,如果沒有客戶的新需求,開發該是一件多么美好的事情。

但是抱怨歸抱怨,客戶就是上帝,你還是只能硬這頭皮把事情做完。
那現在讓我們來考慮如果實現這一功能。

一開始想到的當然是在我們的CPic_Base基類中增加一個接口,比如
String GetStructXMLString();

但是面向對像的設計原則告訴我們,接口不該隨便改動,實際上次CPic_Base里為UnFrame而增加的CRefPtr<CPic_Base> GetUnFrame()接口已經讓你覺得很不爽,感覺這個接口和我們的圖像對象沒有直接關系。

那么我們是否考慮可以重構CPic_Base接口,讓它能以插件的形式實現各種功能,也就是說我們的類層次這里是固定的,但是方法卻可以一直增加而不影響原有的代碼。

這時我們想到了Visitor模式,它基本上是為我們這類需求而量身定做的。
對于Visitor模式的架構,基本上是固定的,定義個IPic_Visitor

class IPic_Visitor
{
public:
    
virtual void VisitPicString(CPic_String& pic) {};
    
virtual void VisitPicFrame(CPic_Frame& pic) {} ;
    
virtual void VisitPicVCat(CPic_VCat& pic) {};
    
virtual void VisitPicHCat(CPic_HCat& pic) {};

    
virtual ~IPic_Visitor() {}
};


在我們的CPic_Base基類里增加一個Accept接口virtual void Accept(IPic_Visitor& visitor) = 0;
這樣圖像對象就可以讓各種類型的Visitor訪問了,各個圖像類的實現也很簡單:

void CPic_String::Accept(IPic_Visitor& visitor)
{
    visitor.VisitPicString(
*this);
}
void CPic_Frame::Accept(IPic_Visitor& visitor)
{
    visitor.VisitPicFrame(
*this);
}
void CPic_VCat::Accept(IPic_Visitor& visitor)
{
    visitor.VisitPicVCat(
*this);
}
void CPic_HCat::Accept(IPic_Visitor& visitor)
{
    visitor.VisitPicHCat(
*this);
}


好了,現在我們用一個新Visitor來改寫我們原來的UnFrame功能,

class CUnFrameVisitor: public IPic_Visitor
{
public:
    
virtual void VisitPicFrame(CPic_Frame& pic);

public:
    CRefPtr
<CPic_Base> GetUnFrameResult();

protected:
    CRefPtr
<CPic_Base> m_picRet;
};

因為Visitor方法都是沒有返回值,參數也是固定的,所以一般都是通過在Visitor里保存成員變量和返回接口來實現返回值的。
這樣實現就很簡單了:

void CUnFrameVisitor::VisitPicFrame(CPic_Frame& pic)
{
    m_picRet 
= pic.m_pic;
}

CRefPtr
<CPic_Base> CUnFrameVisitor::GetUnFrameResult()
{
    
return m_picRet;
}

 

可以看到只有訪問 CPic_Frame才有非空的返回值;其他都是用默認的空方法,最終返回的也就空對象。

這樣我們在最終暴露的CPicture里實現UnFrame也就很簡單了:

bool CPicture::UnFrame()
{
    CUnFrameVisitor vistor;
    m_pic
->Accept(vistor);

    CRefPtr
<CPic_Base> pRet = vistor.GetUnFrameResult();
    
if(!pRet.IsNull())
    {
        m_pic 
= pRet;
    }

    
return !pRet.IsNull();
}


接下來我們考慮如何實現客戶的要求返回XML串的需求,實際上我們前面的Visitor模式已經為我們準備好了條件,我們只需要新增加一個Visitor

class CStructXMLVisitor: public IPic_Visitor
{
public:
    
virtual void VisitPicString(CPic_String& pic);
    
virtual void VisitPicFrame(CPic_Frame& pic);
    
virtual void VisitPicVCat(CPic_VCat& pic);
    
virtual void VisitPicHCat(CPic_HCat& pic);

public:
    std::
string GetStructXMLString() { return m_strStructXML;}

protected:
    std::
string m_strStructXML;
};


實現也不復雜:

void CStructXMLVisitor::VisitPicString(CPic_String& pic)
{
    m_strStructXML 
= "<CPic_String>";
    
int nHeight = pic.GetHeight();
    
for(int i=0;i<nHeight; ++i)
    {
        m_strStructXML 
+= pic.GetLineData(i);
    }
    m_strStructXML 
+= "</CPic_String>";
}

void CStructXMLVisitor::VisitPicFrame(CPic_Frame& pic)
{
    CStructXMLVisitor v;
    pic.m_pic
->Accept(v);
    m_strStructXML 
= "<CPic_Frame>";
    m_strStructXML 
+= v.GetStructXMLString();
    m_strStructXML 
+= "</CPic_Frame>";
}

void CStructXMLVisitor::VisitPicVCat(CPic_VCat& pic)
{
    m_strStructXML 
= "<CPic_VCat>";
    CStructXMLVisitor v1;
    pic.m_pic1
->Accept(v1);
    m_strStructXML 
+= v1.GetStructXMLString();

    CStructXMLVisitor v2;
    pic.m_pic2
->Accept(v2);
    m_strStructXML 
+= v2.GetStructXMLString();

    m_strStructXML 
+= "</CPic_VCat>";
}

void CStructXMLVisitor::VisitPicHCat(CPic_HCat& pic)
{
    m_strStructXML 
= "<CPic_HCat>";
    CStructXMLVisitor v1;
    pic.m_pic1
->Accept(v1);
    m_strStructXML 
+= v1.GetStructXMLString();

    CStructXMLVisitor v2;
    pic.m_pic2
->Accept(v2);
    m_strStructXML 
+= v2.GetStructXMLString();

    m_strStructXML 
+= "</CPic_HCat>";
}


然后我們在我們的CPicture界面里增加一個GetStructXMLString方法,實現也很簡單:

std::string CPicture::GetStructXMLString()
{
    CStructXMLVisitor v;
    m_pic
->Accept(v);
    
return v.GetStructXMLString();
}


可以看到,改用新的設計之后,以后我們再有什么新需求,只要直接增加一個Visitor就好了, 所以說設計不是一層不變的,要根據需求不停的重構。
最后貼一下類圖,外部只要和CPicture打交道就可以了:


 

源代碼下載:  ConsolePicture_1.rar

               ConsolePicture_2.rar

注:(1)該題引自《C++沉思錄》
      (2)C++11里已經有基于引用計數的智能指針share_ptr, 所以以后就不用自己寫了,循環引用的問題也可以通過weak_ptr解決.
posted on 2012-06-12 09:31 Richard Wei 閱讀(5483) 評論(10)  編輯 收藏 引用 所屬分類: 設計模式

FeedBack:
# re: 一道考驗你設計能力的C++編程題
2012-06-12 10:08 | sharkcc
代碼太多,如果考驗設計能力也貼2張類圖上來看看。
是采用裝飾模式嗎  回復  更多評論
  
# re: 一道考驗你設計能力的C++編程題
2012-06-12 10:17 | Richard Wei
@sharkcc
涉及到很多模式, Template, Decorate, Composite, faced, Visitor等
嫌文字太多可以直接下載最后的源代碼看, 相信會有所收獲。  回復  更多評論
  
# re: 一道考驗你設計能力的C++編程題
2012-06-12 10:26 | 小明
最討厭堆砌設計模式,把簡單的問題復雜化  回復  更多評論
  
# re: 一道考驗你設計能力的C++編程題
2012-06-12 10:38 | Richard Wei
@小明
為模式而模式當然不對,但是很多時候你會不自覺地用到設計模式,因為同類問題已經有了一種被大家證明是最合理的方案(模式)來解決。
上面的問題你有更好的解決方案嗎?
  回復  更多評論
  
# re: 一道考驗你設計能力的C++編程題[未登錄]
2012-06-12 11:06 | Simon
樓主的代碼比文字厲害多了  回復  更多評論
  
# re: 一道考驗你設計能力的C++編程題
2012-06-12 11:12 | Richard Wei
@Simon
呵呵, 夸我還是罵我?  回復  更多評論
  
# re: 一道考驗你設計能力的C++編程題
2012-06-12 16:02 | 1111
好像是C++沉思錄里的例子?  回復  更多評論
  
# re: 一道考驗你設計能力的C++編程題
2012-06-13 12:51 | leolai
首先,題目沒有明確給出以后的變化點,你只看題目就想去窮盡封裝所有的變化,這在具體設計中是極其不現實的,封裝變化通常是在變化出現時,而更好的手段是刺激變化的時機,于是就有測試驅動開發,而不是想法預測整個開發中的變化,所以就題目而言,第一種寫法比你后面一堆的代碼好,現階段而言,只要適用就行,當然如果這些變化能明顯觀察到,后面的做法無可厚非  回復  更多評論
  
# re: 一道考驗你設計能力的C++編程題
2012-06-13 13:06 | Richard Wei
@leolai
同意。
一般來說,除非某些設計模式你現在確實適合使用,或是你能預測到今后可能的變化,你才應該使用該模式。
否則還是只要滿足當前的需要就夠了,等確實有新需求時再考慮重構你現有的代碼。比如上面有的 去邊框(unframe), 用XML格式打印圖片的組成結構 等,都是新需求。  回復  更多評論
  
# re: 一道考驗你設計能力的C++編程題
2012-06-20 11:07 | 畢達哥拉斯半圓
不錯  回復  更多評論
  
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            中国日韩欧美久久久久久久久| 欧美一区二区国产| 亚洲啪啪91| 久久精品女人天堂| 国产亚洲欧美一区| 先锋影音久久久| 亚洲尤物影院| 欧美日韩国产二区| 日韩亚洲欧美成人一区| 亚洲激情欧美| 欧美三级中文字幕在线观看| 亚洲午夜精品一区二区| 日韩特黄影片| 欧美亚洲成人网| 欧美亚洲综合网| 性视频1819p久久| 国模大胆一区二区三区| 久热精品视频在线观看一区| 欧美怡红院视频一区二区三区| 国产亚洲二区| 欧美成年网站| 久久久欧美一区二区| 91久久精品一区二区别| 亚洲精品影视| 国产精品一级久久久| 久久久亚洲精品一区二区三区| 久久久久久久久久码影片| 亚洲国产裸拍裸体视频在线观看乱了中文 | 日韩视频永久免费观看| 欧美三级视频在线| 中文国产一区| 性欧美1819sex性高清| 亚洲国产精品久久久久婷婷老年 | 国产亚洲精品自拍| 欧美激情综合色| 国产日本欧美一区二区三区| 亚洲国产视频a| 国产综合一区二区| 在线视频精品| 亚洲最新视频在线| 毛片一区二区三区| 久久久蜜桃一区二区人| 免费日韩精品中文字幕视频在线| 久久精品亚洲精品国产欧美kt∨| 99在线精品视频在线观看| 欧美与黑人午夜性猛交久久久| 中日韩男男gay无套| 牛人盗摄一区二区三区视频| 久久久久国产免费免费| 国产精品视频你懂的| 亚洲乱亚洲高清| 亚洲欧洲精品天堂一级| 久久青青草综合| 久久久水蜜桃av免费网站| 国产精品美女一区二区| 日韩西西人体444www| 亚洲日本一区二区三区| 久久一二三四| 欧美高清视频一二三区| 在线观看日韩av| 久久久久成人网| 久久天天躁狠狠躁夜夜av| 国产视频久久久久| 欧美一区二视频| 久久米奇亚洲| 一区精品在线播放| 久久久亚洲午夜电影| 免费观看日韩| 亚洲精品国产视频| 欧美激情一区二区三区不卡| 亚洲二区免费| 99国产精品99久久久久久| 欧美日韩国产天堂| 正在播放日韩| 久久成人免费| 在线看成人片| 欧美精品一区三区| 一区二区三区久久| 久久久www成人免费精品| 激情小说另类小说亚洲欧美 | 91久久精品国产91久久性色tv| 亚洲精品影院| 国产精品久久福利| 久久精品91| 最新国产拍偷乱拍精品| 亚洲一区中文字幕在线观看| 国产视频一区三区| 狂野欧美激情性xxxx| 99国产精品久久久久久久久久| 亚洲伊人网站| 曰本成人黄色| 欧美性猛交xxxx乱大交退制版| 亚洲欧美成人一区二区三区| 欧美大片91| 先锋影音网一区二区| 亚洲高清不卡在线| 国产精品久久久久久av下载红粉| 久久激情五月丁香伊人| 99视频精品全国免费| 麻豆成人91精品二区三区| 99国产精品自拍| 国产一区二区三区电影在线观看 | 亚洲欧美日韩综合| 亚洲欧洲日本一区二区三区| 欧美一区在线视频| 亚洲理论电影网| 国产一区二区黄| 欧美三级黄美女| 老司机67194精品线观看| 亚洲性夜色噜噜噜7777| 欧美激情网站在线观看| 午夜欧美视频| 一区二区欧美激情| 麻豆国产精品一区二区三区| 亚洲欧美伊人| 亚洲免费精品| 在线精品福利| 国产综合色精品一区二区三区| 欧美视频1区| 欧美激情视频在线免费观看 欧美视频免费一| 亚洲特级片在线| 亚洲精品自在久久| 亚洲福利视频在线| 久久免费视频网站| 欧美伊人久久久久久午夜久久久久| 亚洲美女免费视频| 一区精品久久| 狠久久av成人天堂| 国内精品久久久久影院 日本资源 国内精品久久久久伊人av | 美女日韩欧美| 久久性天堂网| 久久免费精品视频| 久久久综合免费视频| 欧美在线在线| 欧美中文在线观看国产| 午夜视频久久久| 欧美一区二区观看视频| 亚洲欧美日韩在线高清直播| 亚洲午夜精品一区二区三区他趣| 一区二区三区高清视频在线观看| 亚洲精品视频在线| 99精品免费视频| 亚洲天堂av在线免费| 亚洲在线观看视频| 午夜精品久久久久久久久久久久| 亚洲你懂的在线视频| 欧美在线3区| 久久婷婷麻豆| 欧美电影美腿模特1979在线看| 欧美电影在线免费观看网站| 欧美成人亚洲成人| 亚洲精品欧美精品| 中文一区在线| 欧美在线1区| 久久综合久久综合久久综合| 欧美二区在线观看| 欧美日韩在线不卡| 国产欧美日韩在线播放| 在线不卡亚洲| 99在线热播精品免费| 午夜在线视频一区二区区别 | 91久久夜色精品国产网站| 99riav国产精品| 欧美一区二区三区播放老司机| 久久久99免费视频| 亚洲国产精品热久久| 日韩一二三在线视频播| 香蕉国产精品偷在线观看不卡| 久久久久9999亚洲精品| 欧美日本不卡| 国产婷婷一区二区| 99国产精品国产精品久久| 午夜精品美女久久久久av福利| 久久亚洲图片| 一本久道综合久久精品| 久久精品女人| 国产精品jvid在线观看蜜臀| 国产又爽又黄的激情精品视频 | 亚洲大胆女人| 亚洲综合视频网| 欧美国产日韩精品| 欧美亚洲综合网| 欧美另类变人与禽xxxxx| 国产亚洲精品7777| 一本色道久久综合| 久久婷婷影院| 亚洲中无吗在线| 欧美国内亚洲| 欧美午夜欧美| 国产精品草莓在线免费观看| 在线电影欧美日韩一区二区私密| 中文国产成人精品| 农村妇女精品| 欧美在线播放高清精品| 欧美香蕉大胸在线视频观看| 亚洲区第一页| 免费久久久一本精品久久区| 午夜欧美视频| 国产精品入口尤物| 亚洲性人人天天夜夜摸|