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

陳碩的Blog

C++ 工程實(shí)踐(3):采用有利于版本管理的代碼格式

陳碩 (giantchen_AT_gmail)

Blog.csdn.net/Solstice

版本管理(version controlling)是每個(gè)程序員的基本技能,C++ 程序員也不例外。版本管理的基本功能之一是追蹤代碼變化,讓你能清楚地知道代碼是如何一步步變成現(xiàn)在的這個(gè)樣子,以及每次 check-in 都具體改動(dòng)了哪些內(nèi)部。無(wú)論是傳統(tǒng)的集中式版本管理工具,如 Subversion,還是新型的分布式管理工具,如 Git/Hg,比較兩個(gè)版本(revision)的差異都是其基本功能,即俗稱(chēng)“做一下 diff”。

diff 的輸出是個(gè)窺孔(peephole),它的上下文有限(diff –u 默認(rèn)顯示前后 3 行)。在做 code review 的時(shí)候,如果能憑這“一孔之見(jiàn)”就能發(fā)現(xiàn)代碼改動(dòng)有問(wèn)題,那就再好也不過(guò)了。

 

C 和 C++ 都是自由格式的語(yǔ)言,代碼中的換行符被當(dāng)做 white space 來(lái)對(duì)待。(當(dāng)然,我們說(shuō)的是預(yù)處理(preprocess)之后的情況)。對(duì)編譯器來(lái)說(shuō)一模一樣的代碼可以有多種寫(xiě)法,比如

foo(1, 2, 3, 4);

foo(1,

    2,

    3,

    4);

詞法分析的結(jié)果是一樣的,語(yǔ)意也完全一樣。

對(duì)人來(lái)說(shuō),這兩種寫(xiě)法讀起來(lái)不一樣,對(duì)與版本管理工具來(lái)說(shuō),同樣功能的修改造成的差異(diff)也往往不一樣。所謂“有利于版本管理”,就是指在代碼中合理使用換行符,對(duì) diff 工具友好,讓 diff 的結(jié)果清晰明了地表達(dá)代碼的改動(dòng)。(diff 一般以行為單位,也可以以單詞為單位,本文只考慮最常見(jiàn)的 diff by lines。)

這里舉一些例子。

對(duì) diff 友好的代碼格式

1. 多行注釋也用 //,不用 /* */

Scott Meyers 寫(xiě)的《Effective C++》第二版第 4 條建議使用 C++ 風(fēng)格,我這里為他補(bǔ)充一條理由:對(duì) diff 友好。比如,我要注釋一大段代碼(其實(shí)這不是個(gè)好的做法,但是在實(shí)踐中有時(shí)會(huì)遇到),如果用 /* */,那么得到的 diff 是:

diff --git a/examples/asio/tutorial/timer5/timer.cc b/examples/asio/tutorial/timer5/timer.cc
--- a/examples/asio/tutorial/timer5/timer.cc
+++ b/examples/asio/tutorial/timer5/timer.cc
@@ -18,6 +18,7 @@ class Printer : boost::noncopyable
loop2_->runAfter(1, boost::bind(&Printer::print2, this));
}
+  /*
~Printer()
{
std::cout << "Final count is " << count_ << "\n";
@@ -38,6 +39,7 @@ class Printer : boost::noncopyable
loop1_->quit();
}
}
+  */

void print2()
{

從這樣的 diff output 能看出注釋了哪些代碼嗎?

如果用 //,結(jié)果會(huì)清晰很多:

diff --git a/examples/asio/tutorial/timer5/timer.cc b/examples/asio/tutorial/timer5/timer.cc
--- a/examples/asio/tutorial/timer5/timer.cc
+++ b/examples/asio/tutorial/timer5/timer.cc
@@ -18,26 +18,26 @@ class Printer : boost::noncopyable
loop2_->runAfter(1, boost::bind(&Printer::print2, this));
}
-  ~Printer()
-  {
-    std::cout << "Final count is " << count_ << "\n";
-  }
+  // ~Printer()
+  // {
+  //   std::cout << "Final count is " << count_ << "\n";
+  // }
-  void print1()
-  {
-    muduo::MutexLockGuard lock(mutex_);
-    if (count_ < 10)
-    {
-      std::cout << "Timer 1: " << count_ << "\n";
-      ++count_;
-
-      loop1_->runAfter(1, boost::bind(&Printer::print1, this));
-    }
-    else
-    {
-      loop1_->quit();
-    }
-  }
+  // void print1()
+  // {
+  //   muduo::MutexLockGuard lock(mutex_);
+  //   if (count_ < 10)
+  //   {
+  //     std::cout << "Timer 1: " << count_ << "\n";
+  //     ++count_;
+  //
+  //     loop1_->runAfter(1, boost::bind(&Printer::print1, this));
+  //   }
+  //   else
+  //   {
+  //     loop1_->quit();
+  //   }
+  // }
void print2()
{

同樣的道理,取消注釋的時(shí)候 // 也比 /* */ 更清晰。

另外,如果用 /* */ 來(lái)做多行注釋?zhuān)瑥?diff 不一定能看出來(lái)你是在修改代碼還是修改注釋。比如以下 diff 似乎修改了 muduo::EventLoop::runAfter 的調(diào)用參數(shù):

diff --git a/examples/asio/tutorial/timer5/timer.cc b/examples/asio/tutorial/timer5/timer.cc
--- a/examples/asio/tutorial/timer5/timer.cc
+++ b/examples/asio/tutorial/timer5/timer.cc
@@ -32,7 +32,7 @@ class Printer : boost::noncopyable
std::cout << "Timer 1: " << count_ << "\n";
++count_;
-      loop1_->runAfter(1, boost::bind(&Printer::print1, this));
+      loop1_->runAfter(2, boost::bind(&Printer::print1, this));
}
else
{

其實(shí)這個(gè)修改發(fā)生在注釋里邊 (要增加上下文才能看到, diff -U 20,多一道手續(xù),降低了工作效率),對(duì)代碼行為沒(méi)有影響:

diff --git a/examples/asio/tutorial/timer5/timer.cc b/examples/asio/tutorial/timer5/timer.cc
--- a/examples/asio/tutorial/timer5/timer.cc
+++ b/examples/asio/tutorial/timer5/timer.cc
@@ -20,31 +20,31 @@ class Printer : boost::noncopyable
   /*
   ~Printer()
{
std::cout << "Final count is " << count_ << "\n";
}
void print1()
{
muduo::MutexLockGuard lock(mutex_);
if (count_ < 10)
{
std::cout << "Timer 1: " << count_ << "\n";
++count_;
-      loop1_->runAfter(1, boost::bind(&Printer::print1, this));
+      loop1_->runAfter(2, boost::bind(&Printer::print1, this));
}
else
{
loop1_->quit();
}
}
   */

void print2()
{
muduo::MutexLockGuard lock(mutex_);
if (count_ < 10)
{
std::cout << "Timer 2: " << count_ << "\n";
++count_;

總之,不要用 /* */ 來(lái)注釋多行代碼。

或許是時(shí)過(guò)境遷,大家都在用 // 注釋了,《Effective C++》第三版去掉了這一條建議。

2. 局部變量與成員變量的定義

基本原則是,一行代碼只定義一個(gè)變量,比如

double x;

double y;

將來(lái)代碼增加一個(gè) double z 的時(shí)候,diff 輸出一眼就能看出改了什么:

@@ -63,6 +63,7 @@ private:
int count_;
double x;
double y;
+  double z;
};
int main()

如果把 x 和 y 寫(xiě)在一行,diff 的輸出就得多看幾眼才知道。

@@ -61,7 +61,7 @@ private:
muduo::net::EventLoop* loop1_;
muduo::net::EventLoop* loop2_;
int count_;
-  double x, y;
+  double x, y, z;
 };
int main()

所以,一行只定義一個(gè)變量更利于版本管理。同樣的道理適用于 enum 成員的定義,數(shù)組的初始化列表等等。

3. 函數(shù)聲明中的參數(shù)

如果函數(shù)的參數(shù)大于 3 個(gè),那么在逗號(hào)后面換行,這樣每個(gè)參數(shù)占一行,便于 diff。以 muduo::net::TcpClient 為例:

class TcpClient : boost::noncopyable
{
public:
 TcpClient(EventLoop* loop,
const InetAddress& serverAddr,
const string& name);

如果將來(lái) TcpClient 的構(gòu)造函數(shù)增加或修改一個(gè)參數(shù),那么很容易從 diff 看出來(lái)。這恐怕比在一行長(zhǎng)代碼里數(shù)逗號(hào)要高效一些。

4. 函數(shù)調(diào)用時(shí)的參數(shù)

在函數(shù)調(diào)用的時(shí)候,如果參數(shù)大于 3 個(gè),那么把實(shí)參分行寫(xiě)。以 muduo::net::EPollPoller 為例:

Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels)
{
int numEvents = ::epoll_wait(epollfd_,
&*events_.begin(),
static_cast<int>(events_.size()),
timeoutMs);
Timestamp now(Timestamp::now());

這樣一來(lái),如果將來(lái)重構(gòu)引入了一個(gè)新參數(shù)(好吧,epoll_wait 不會(huì)有這個(gè)問(wèn)題),那么函數(shù)定義和函數(shù)調(diào)用的地方的 diff 具有相同的形式(比方說(shuō)都是在倒數(shù)第二行加了一行內(nèi)容),很容易肉眼驗(yàn)證有沒(méi)有錯(cuò)位。如果參數(shù)寫(xiě)在一行里邊,就得睜大眼睛數(shù)逗號(hào)了。

5. class 初始化列表的寫(xiě)法

同樣的道理,class 初始化列表(initializer list)也遵循一行一個(gè)的原則,這樣將來(lái)如果加入新的成員變量,那么兩處(class 定義和 ctor 定義)的 diff 具有相同的形式,讓錯(cuò)誤無(wú)所遁形。以 muduo::net::Buffer 為例:

class Buffer : public muduo::copyable
{
public:
static const size_t kCheapPrepend = 8;
static const size_t kInitialSize = 1024;
Buffer()
    : buffer_(kCheapPrepend + kInitialSize),
readerIndex_(kCheapPrepend),
writerIndex_(kCheapPrepend)
{
}
// 省略
 private:
   std::vector<char> buffer_;
size_t readerIndex_;
size_t writerIndex_;
static const char kCRLF[];
};

注意,初始化列表的順序必須和數(shù)據(jù)成員聲明的順序相同。

6. 與 namespace 有關(guān)的縮進(jìn)

Google 的 C++ 編程規(guī)范明確指出,namespace 不增加縮進(jìn)。這么做非常有道理,方便 diff –p 把函數(shù)名顯示在每個(gè) diff chunk 的頭上。

如果對(duì)函數(shù)實(shí)現(xiàn)做 diff,chunk name 是函數(shù)名,讓人一眼就能看出改的是哪個(gè)函數(shù)。如下圖,紅色劃線部分。

diff_function

如果對(duì) class 做 diff,那么 chunk name 就是 class name。

diff_class

diff 原本是為 C 語(yǔ)言設(shè)計(jì)的,C 語(yǔ)言沒(méi)有 namespace 縮進(jìn)一說(shuō),所以它默認(rèn)會(huì)找到“頂格寫(xiě)”的函數(shù)作為一個(gè) diff chunk 的名字,如果函數(shù)名前面有空格,它就不認(rèn)得了。muduo 的代碼都遵循這一規(guī)則,例如:

namespace muduo
{
///
/// Time stamp in UTC, in microseconds resolution.
///
/// This class is immutable.
/// It's recommended to pass it by value, since it's passed in register on x64.
///
class Timestamp : public muduo::copyable,
public boost::less_than_comparable<Timestamp>
{
// class 從第一列開(kāi)始寫(xiě),不縮進(jìn)
// 函數(shù)的實(shí)現(xiàn)也從第一列開(kāi)始寫(xiě),不縮進(jìn)。
Timestamp Timestamp::now()
{
struct timeval tv;
gettimeofday(&tv, NULL);
int64_t seconds = tv.tv_sec;
return Timestamp(seconds * kMicroSecondsPerSecond + tv.tv_usec);
}

相反,boost 中的某些庫(kù)的代碼是按 namespace 來(lái)縮進(jìn)的,這樣的話看 diff 往往不知道改動(dòng)的是哪個(gè) class 的哪個(gè)成員函數(shù)。

這個(gè)或許可以通過(guò)設(shè)置 diff 取函數(shù)名的正則表達(dá)式來(lái)解決,但是如果我們寫(xiě)代碼的時(shí)候就注意把函數(shù)“頂格寫(xiě)”,那么就不用去動(dòng) diff 的默認(rèn)設(shè)置了。另外,正則表達(dá)式不能完全匹配函數(shù)名,因?yàn)楹瘮?shù)名是上下文無(wú)關(guān)語(yǔ)法(context-free syntax),你沒(méi)辦法寫(xiě)一個(gè)正則語(yǔ)法去匹配上下文無(wú)關(guān)語(yǔ)法。我總能寫(xiě)出某種函數(shù)聲明,讓你的正則表達(dá)式失效(想想函數(shù)的返回類(lèi)型,它可能是一個(gè)非常復(fù)雜的東西,更別說(shuō)參數(shù)了)。更何況 C++ 的語(yǔ)法是上下文相關(guān)的,比如你猜 Foo<Bar> qux; 是個(gè)表達(dá)式還是變量定義?

7. public 與 private

我認(rèn)為這是 C++ 語(yǔ)法的一個(gè)缺陷,如果我把一個(gè)成員函數(shù)從 public 區(qū)移到 private 區(qū),那么從 diff 上看不出來(lái)我干了什么,例如:

diff --git a/muduo/net/TcpClient.h b/muduo/net/TcpClient.h
--- a/muduo/net/TcpClient.h
+++ b/muduo/net/TcpClient.h
@@ -37,7 +37,6 @@ class TcpClient : boost::noncopyable
void connect();
void disconnect();
-  bool retry() const;
   void enableRetry() { retry_ = true; }
/// Set connection callback.
@@ -60,6 +59,7 @@ class TcpClient : boost::noncopyable
void newConnection(int sockfd);
/// Not thread safe, but in loop
void removeConnection(const TcpConnectionPtr& conn);
+  bool retry() const;
EventLoop* loop_;
boost::scoped_ptr<Connector> connector_; // avoid revealing Connector

從上面的 diff 能看出我把 retry() 變成 private 了嗎?對(duì)此我也沒(méi)有好的解決辦法,總不能每個(gè)函數(shù)前面都寫(xiě)上 public: 或 private: 吧?

對(duì)此 Java 和 C# 都做得比較好,它們把 public/private 等修飾符放到每個(gè)成員函數(shù)的定義中。這么做增加了信息的冗余度,讓 diff 的結(jié)果更直觀。

 

歡迎補(bǔ)充。

對(duì) grep 友好的代碼風(fēng)格

操作符重載

C++工具匱乏,在一個(gè)項(xiàng)目里,要找到一個(gè)函數(shù)的定義或許不算太難(最多就是分析一下重載和模板特化),但是要找到一個(gè)函數(shù)的使用就難多了。不比 Java,在 Eclipse 里 Ctrl+Shift+G 就能找到所有的引用點(diǎn)。

假如我要做一個(gè)重構(gòu),想先找到代碼里所有用到 muduo::timeDifference 的地方,判斷一下工作是否可行,基本上惟一的辦法是grep。用 grep 還不能排除同名的函數(shù)和注釋里的內(nèi)容。這也說(shuō)明為什么要用 // 來(lái)引導(dǎo)注釋?zhuān)驗(yàn)樵?grep 的時(shí)候,一眼就能看出這行代碼是在注釋里的。

在我看來(lái),operator overloading 應(yīng)僅限于和 STL algorithm/container 配合時(shí)使用,比如 transform() 和 map<T,U>,其他情況都用具名函數(shù)為宜。原因之一是,我根本用 grep 找不到在哪兒用到了 operator-()。這也是 muduo::Timestamp 只提供 operator<() 而不提供 operator+() operator-() 的原因,我提供了兩個(gè)函數(shù) timeDifference 和 addTime 來(lái)實(shí)現(xiàn)所需的功能。

又比如,Google Protocol Buffers 的回調(diào)是 class Closure,它的接口用的是 virtual function Run() 而不是 virtual operator()()。

static_cast 與 C-style cast

為什么 C++ 要引入 static_cast 之類(lèi)的轉(zhuǎn)型操作符,原因之一就是像 (int*) pBuffer 這樣的表達(dá)式基本上沒(méi)辦法用 grep 判斷出它是個(gè)強(qiáng)制類(lèi)型轉(zhuǎn)換,寫(xiě)不出一個(gè)剛好只匹配類(lèi)型轉(zhuǎn)換的正則表達(dá)式。(again,語(yǔ)法是上下文無(wú)關(guān)的,無(wú)法用正則搞定。)

如果類(lèi)型轉(zhuǎn)換都用 *_cast,那只要 grep 一下我就能知道代碼里哪兒用了 reinterpret_cast 轉(zhuǎn)換,便于迅速地檢查有沒(méi)有用錯(cuò)。為了強(qiáng)調(diào)這一點(diǎn),muduo 開(kāi)啟了編譯選項(xiàng) -Wold-style-cast 來(lái)幫助查找 C-style cast,這樣在編譯時(shí)就能幫我們找到問(wèn)題。

 一切為了效率

如果用圖形化的文件比較工具,似乎能避免上面列舉的問(wèn)題。但無(wú)論是 web 還是客戶端,無(wú)論是 inline diff 還是 diff by lines 都不能解決全部問(wèn)題,效率也不一定更高。

對(duì)于(2),如果想知道是誰(shuí)在什么時(shí)候增加的 double z,在分行寫(xiě)的情況下,用 git blame 或 svn blame 立刻就能找到始作俑者。如果寫(xiě)成一行,那就得把文件的 revisions 拿來(lái)一個(gè)個(gè)人工比較,因?yàn)檫@一行 double x = 0.0, y = 1.0, z = -1.0; 可能修改過(guò)多次,你得一個(gè)個(gè)看才知道什么時(shí)候加入了變量 z。這個(gè) blame 的 case 也適用于 3、4、5。

比如(6)改動(dòng)了一行代碼,你還是要 scroll up 去找改的是哪個(gè) function,人眼看的話還有“看走眼”的可能,又得再定睛觀瞧。這一切都是浪費(fèi)人的時(shí)間,使用更好的圖形化工具并不能減少浪費(fèi),相反,我認(rèn)為增加了浪費(fèi)。

另外一個(gè)常見(jiàn)的工作場(chǎng)景,早上來(lái)到辦公室,update 一下代碼,然后掃一眼 diff output 看看別人昨天動(dòng)了哪些文件,改了哪些代碼,這就是一兩條命令的事,幾秒鐘就能解決戰(zhàn)斗。如果用圖形化的工具,得一個(gè)個(gè)點(diǎn)開(kāi)文件 diff 的鏈接或點(diǎn)開(kāi)新 tab 來(lái)看文件的 side-by-side 比較(不這么做的話看不到足夠多的上下文,跟看 diff output 無(wú)異),然后點(diǎn)擊鼠標(biāo)滾動(dòng)頁(yè)面去看別人到底改了什么。說(shuō)實(shí)話我覺(jué)得這么做效率不比 diff 高。

(待續(xù))

posted on 2011-03-05 15:16 陳碩 閱讀(3380) 評(píng)論(7)  編輯 收藏 引用

評(píng)論

# re: C++ 工程實(shí)踐(3):采用有利于版本管理的代碼格式 2011-03-05 21:43 陳梓瀚(vczh)

工具不行,就換一個(gè)/自己做一個(gè)嘛。這樣才能進(jìn)步。  回復(fù)  更多評(píng)論   

# re: C++ 工程實(shí)踐(3):采用有利于版本管理的代碼格式 2011-03-06 18:38 classyk

我建議多行還是用/* */來(lái)注釋?zhuān)M管現(xiàn)在很多的編輯器已經(jīng)很方便的可以多行//注釋。

因?yàn)?/ * */框住的代碼,意味著是一個(gè)相關(guān)聯(lián)的段落
而多行//則表示不了這種意思。

  回復(fù)  更多評(píng)論   

# re: C++ 工程實(shí)踐(3):采用有利于版本管理的代碼格式 2011-03-06 19:52 陳碩

@classyk
這個(gè)好吧,用縮進(jìn)。注釋函數(shù)的時(shí)候把 // 放第一列,注釋 for 循環(huán)的時(shí)候把 // 與 for 上面一行語(yǔ)句對(duì)齊。  回復(fù)  更多評(píng)論   

# re: C++ 工程實(shí)踐(3):采用有利于版本管理的代碼格式 2011-03-10 17:01 violet

聲明變量時(shí)分行寫(xiě),代碼很不好看啊。
為了diff方便而降低了代碼可讀性,不太好吧  回復(fù)  更多評(píng)論   

# re: C++ 工程實(shí)踐(3):采用有利于版本管理的代碼格式 2011-03-10 19:48 陳碩

@violet
你的意思是
string name;
string address;

string name, address;
要難看?  回復(fù)  更多評(píng)論   

# re: C++ 工程實(shí)踐(3):采用有利于版本管理的代碼格式 2011-03-21 22:03 3tgame

@@ -37,7 +37,6 @@ class TcpClient : boost::noncopyable
-37,7 +37,6 表示啥意思?怎么跟默認(rèn)的linux的diff輸出信息不一樣  回復(fù)  更多評(píng)論   

# re: C++ 工程實(shí)踐(3):采用有利于版本管理的代碼格式 2011-03-22 08:20 陳碩

diff -u
http://en.wikipedia.org/wiki/Diff#Unified_format  回復(fù)  更多評(píng)論   


只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


<2011年5月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

導(dǎo)航

統(tǒng)計(jì)

常用鏈接

隨筆分類(lèi)

隨筆檔案

相冊(cè)

搜索

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            黄色成人免费网站| 老司机aⅴ在线精品导航| 欧美屁股在线| 亚洲电影有码| 99re视频这里只有精品| 欧美激情一区| 夜久久久久久| 性欧美激情精品| 国产精品日韩二区| 亚洲人成人一区二区在线观看| 亚洲二区精品| 欧美日韩国产小视频| 日韩午夜电影av| 一区二区三区国产在线观看| 欧美日韩亚洲一区在线观看| 中文在线一区| 久久久久天天天天| 亚洲国产成人午夜在线一区| 欧美成人综合一区| 夜夜爽99久久国产综合精品女不卡| 亚洲一区二区在线播放| 国产精品影音先锋| 久久精品亚洲| 最近中文字幕日韩精品| 亚洲欧美日韩网| 国产情人综合久久777777| 久久精品日韩欧美| 久久一区二区三区国产精品| 激情视频一区| 久久久久久自在自线| 亚洲大片在线| 欧美一区二区三区视频在线| 在线看国产日韩| 国产精品高潮呻吟久久| 中文在线一区| 麻豆91精品| 正在播放欧美视频| 国产在线高清精品| 欧美一激情一区二区三区| 欧美国产一区二区| 欧美亚洲在线播放| 亚洲精品一区二区三区蜜桃久| 欧美理论在线播放| 欧美一区二区免费| 亚洲三级电影在线观看 | 激情成人av在线| 欧美国产精品中文字幕| 日韩视频一区二区在线观看| 久久精品免费播放| 中国女人久久久| 国产欧美精品一区二区三区介绍| 久久国产日韩欧美| 欧美成人精品一区二区| 亚洲欧美日韩国产中文在线| 国产综合色产| 欧美日韩亚洲一区二区三区四区| 久久精品最新地址| 亚洲一区二区在线免费观看视频| 久久久蜜桃一区二区人| 亚洲深夜影院| 亚洲精品国产精品久久清纯直播| 国产亚洲欧美色| 欧美久久一级| 久久躁狠狠躁夜夜爽| 欧美亚洲系列| 亚洲一区二区在线视频 | 欧美成人午夜免费视在线看片| 亚洲尤物在线视频观看| 亚洲精品乱码久久久久久黑人| 国产一区二区三区网站| 国产精品国产精品国产专区不蜜| 欧美a级片一区| 久久精品免费看| 亚洲一区二区欧美日韩| 这里是久久伊人| 99re66热这里只有精品3直播| 亚洲承认在线| 欧美日韩在线播| 欧美成人四级电影| 久久久久免费视频| 欧美在线网站| 久久精品久久综合| 久久精品理论片| 久久久久久久久伊人| 久久精品亚洲| 久久xxxx精品视频| 久久伊人精品天天| 久久中文精品| 裸体丰满少妇做受久久99精品| 久久久人成影片一区二区三区 | 欧美精品少妇一区二区三区| 欧美成人午夜视频| 欧美精品久久久久久| 欧美激情中文字幕一区二区| 欧美精品三区| 欧美成人免费播放| 欧美欧美午夜aⅴ在线观看| 欧美精品97| 欧美日韩免费一区| 欧美精品在线观看| 欧美性一区二区| 国产精品爽爽爽| 国产在线拍偷自揄拍精品| 国内精品久久久| 亚洲韩国日本中文字幕| 亚洲激情电影中文字幕| 亚洲图片激情小说| 久久国产精品久久精品国产| 久久尤物视频| 亚洲精品乱码久久久久久| 一区二区三区四区蜜桃| 日韩亚洲欧美精品| 欧美一级欧美一级在线播放| 麻豆乱码国产一区二区三区| 久久男人av资源网站| 欧美国产日韩一区二区| 国产精品久久久久av免费| 国产日韩欧美自拍| 亚洲国产高清自拍| 亚洲影视在线| 久久色在线播放| 亚洲精品视频免费| 亚洲欧美成人网| 久久综合色婷婷| 欧美视频在线不卡| 国产精品爽爽爽| 在线观看成人网| 亚洲小少妇裸体bbw| 久久最新视频| 一区二区三区高清在线| 久久久久国产精品人| 欧美日韩亚洲天堂| 尤物99国产成人精品视频| 亚洲视频久久| 麻豆91精品91久久久的内涵| 欧美激情第五页| 亚洲在线视频网站| 欧美激情一区二区三区高清视频| 国产精品影片在线观看| 亚洲美女中文字幕| 玖玖玖免费嫩草在线影院一区| 亚洲啪啪91| 久久精品国产99精品国产亚洲性色 | 免费观看成人鲁鲁鲁鲁鲁视频| 国产精品国产精品国产专区不蜜| 亚洲国产一区二区a毛片| 午夜一级在线看亚洲| 亚洲国产精品久久久久秋霞影院| 亚洲私人影吧| 欧美一区二区精品久久911| 欧美国产在线视频| 激情综合网激情| 久久狠狠亚洲综合| 一区二区三区蜜桃网| 欧美精品一区二区三区蜜桃| 影音先锋成人资源站| 欧美在线视频全部完| 一本色道**综合亚洲精品蜜桃冫| 久久夜色精品国产亚洲aⅴ| 欧美日韩亚洲视频一区| 狠狠色狠狠色综合日日tαg| 亚洲欧美精品suv| 亚洲精品视频一区| 欧美高清自拍一区| 亚洲高清色综合| 久久午夜精品一区二区| 午夜免费电影一区在线观看| 国产精品久久| 中文欧美在线视频| 亚洲国产精品久久久久秋霞不卡| 久久久久久久久久久久久久一区 | 性欧美办公室18xxxxhd| 这里只有精品电影| 欧美日韩视频在线第一区| 亚洲韩日在线| 亚洲高清一区二| 欧美精品18videos性欧美| 亚洲精品网站在线播放gif| 亚洲电影av| 久久久久久久网| 亚洲欧洲一二三| 亚洲人成网站999久久久综合| 欧美成人一区二区| 夜夜嗨一区二区| 在线亚洲高清视频| 国产老肥熟一区二区三区| 欧美在线free| 亚洲视屏一区| 在线免费不卡视频| 亚洲国产99精品国自产| 欧美欧美天天天天操| 一区二区三区欧美成人| 亚洲特黄一级片| 国产一区再线| 欧美大片一区| 欧美日韩在线三区| 久久国产精品久久久久久久久久| 欧美一区久久| 在线免费观看欧美| 一个人看的www久久|