成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

C++ 引用的前世今生:為什么說它不只是指針的"語法糖"?

開發(fā)
在 C++ 里,引用就像是給變量起的"綽號"或"別名"。當(dāng)你通過這個"綽號"做任何事情時,實(shí)際上是在操作原來的那個變量。

哈嘍,大家好,我是小康。

還記得你第一次遇到 C++ 引用時的樣子嗎?我反正記得清清楚楚 —— 那感覺就像第一次看到魔術(shù)師從空帽子里拽出一只兔子一樣困惑又震驚:

"啥?這東西看起來像變量,用起來也像變量,但它實(shí)際上是別人的分身?而且跟指針有啥區(qū)別?這不就是指針換了個馬甲嗎?為啥 C++ 要搞這么復(fù)雜?"

如果你也有過這樣的疑惑,或者正在被引用和指針搞得頭大,那今天這篇文章就是為你準(zhǔn)備的!我保證用最簡單、最有趣的方式讓你徹底理解這個讓無數(shù)新手頭疼的概念。

我們不玩那些高深莫測的理論,就用大白話聊聊:引用到底是個啥玩意兒?它跟指針有什么本質(zhì)區(qū)別?為什么要有它?以及——它真的只是指針的"語法糖"那么簡單嗎?

準(zhǔn)備好你的爆米花,我們開始這場"揭秘"之旅吧!

一、引用是啥?用大白話怎么解釋?

想象一下這個場景:

小明有個很漂亮的游戲機(jī),他的好朋友小紅特別想玩。小明可以有三種方式讓小紅也能使用這個游戲機(jī):

  • 給小紅一個完整復(fù)制品(傳值)—— "給你做一個一模一樣的"
  • 告訴小紅游戲機(jī)放在哪個柜子的哪個抽屜里(指針)—— "我告訴你位置,你自己去拿"
  • 給小紅起個別名,說"以后你也可以叫這個游戲機(jī)小花"(引用)—— "這就是你的了,但實(shí)際上還是我的那個"

在 C++ 里,引用就像是給變量起的"綽號"或"別名"。當(dāng)你通過這個"綽號"做任何事情時,實(shí)際上是在操作原來的那個變量。

int original = 42;   // 原始變量
int &ref = original; // ref是original的"綽號"

ref = 100;           // 通過"綽號"修改值
cout << original;    // 輸出100,原始變量也被修改了

這里ref不是新變量,它只是original的另一個名字。你通過ref所做的任何操作,實(shí)際上都是在操作original。這就是引用的基本概念。

二、為啥要搞個引用出來?C語言不是活得好好的嗎?

是的,在 C 語言中我們只有指針沒有引用,照樣把 Linux內(nèi)核 寫出來了。那為啥 C++ 還要引入引用這個概念呢?

這就要從 C++ 的設(shè)計(jì)哲學(xué)說起了。C++的發(fā)明者 Bjarne Stroustrup 希望保留 C 語言的高效率,同時提供更高層次的抽象。而引用,正是這種抽象的產(chǎn)物之一。

引入引用的主要原因:

  • 簡化代碼 - 不用像指針那樣需要解引用操作(*)
  • 增強(qiáng)安全性 - 引用必須初始化,不能為空,不能改變指向
  • 支持操作符重載 - 引用使得自定義類型的操作符重載更加直觀
  • 支持更自然的語法 - 讓復(fù)雜的操作看起來更簡單明了

拿我們常用的cin和cout來說,你有沒有想過為什么可以這樣鏈?zhǔn)秸{(diào)用?

cout << "Hello" << " " << "World";

這背后用的就是引用返回!如果沒有引用,這種流暢的語法就很難實(shí)現(xiàn)。

三、"不就是指針嗎?"才不是呢!

很多人會說:"引用不就是指針換了個寫法嗎?有必要搞這么復(fù)雜?"

表面上看是有點(diǎn)像,但它們可是兩個完全不同的"物種"!就像貓和老虎看起來都是貓科動物,但你絕對不會把家里的寵物貓和動物園里的老虎混為一談吧?

來看看它們的關(guān)鍵區(qū)別:

1. 指針可以到處"浪",引用必須"從一而終"

int a = 5;
int b = 10;

int *ptr = &a;  // 指針指向a
ptr = &b;       // 改變主意,指向b了
                // 指針:"今天看你順眼就指向你~"

int &ref = a;   // 引用綁定到a
// ref = &b;    // 錯誤!引用不能重新綁定
                // 引用:"一旦認(rèn)定,終身不變"

引用一旦初始化,就不能改變它所引用的對象。這聽起來是個限制,但實(shí)際上這種"專一"帶來了更多的安全性和可靠性。

2. 指針可以指向"虛無",引用必須有"實(shí)體"

int *ptr = NULL; // 指針可以是NULL
// int &ref;     // 錯誤!引用必須初始化

引用必須在定義時初始化,而且必須引用一個已存在的對象。這避免了空指針導(dǎo)致的崩潰問題。

3. 指針需要"解引用",引用自動"傳送"

int x = 42;
int *ptr = &x;
int &ref = x;

*ptr = 100;  // 指針:得加個*才能改值
ref = 100;   // 引用:直接用就是了

使用引用時,編譯器自動幫你處理了所有的解引用操作,讓代碼更加簡潔。

4. 指針有自己的內(nèi)存地址,引用沒有

int x = 42;
int *ptr = &x;
int &ref = x;

cout << &ptr;  // 輸出ptr自己的地址
cout << &ref;  // 輸出x的地址,不是ref的

引用不占用額外的存儲空間(不絕對,下面會解釋),它只是一個別名。

5. 指針可以有多級,引用只有一級

int x = 42;
int *p = &x;     // 一級指針
int **pp = &p;   // 二級指針,指向指針的指針
int ***ppp = &pp;// 三級指針,指向指針的指針的指針

int &r = x;      // 引用
// int &&rr = r; // 錯誤!C++不支持引用的引用

C++不支持引用的引用(雖然C++11引入了右值引用&&,但那是另一個概念)。

四、揭秘:引用在底層到底是啥?

好了,說了這么多,我們終于要揭開謎底了:在實(shí)現(xiàn)層面,編譯器通常確實(shí)用指針來實(shí)現(xiàn)引用!

但這不代表它們是一回事。就像汽車內(nèi)部有發(fā)動機(jī),但你不會說"汽車就是個發(fā)動機(jī)"一樣。

編譯器會把引用轉(zhuǎn)換成指針,但會:

  • 自動幫你解引用
  • 不允許它為空
  • 不讓它改變指向
  • 優(yōu)化掉不必要的間接尋址

看看這段代碼:

void func(int &a) {
    a = 100;
}

編譯器可能會將其轉(zhuǎn)換為:

void func(int *a) {
    *a = 100;
}

但在調(diào)用處,編譯器會自動傳入地址,而不需要你寫&:

int x = 42;
func(x);  // 編譯器自動轉(zhuǎn)換為func(&x)

編譯器甚至可能進(jìn)一步優(yōu)化,完全消除這個指針!

這也是為什么我之前說引用不一定占用額外的存儲空間 —— 在某些情況下,編譯器可以優(yōu)化掉這個引用,讓它不占用任何額外內(nèi)存。

五、引用的變種:左值引用、右值引用和轉(zhuǎn)發(fā)引用

隨著C++的發(fā)展,引用家族也不斷壯大。C++11引入了右值引用和轉(zhuǎn)發(fā)引用,讓引用系統(tǒng)更加完善。

1. 左值引用 - 最傳統(tǒng)的引用

我們前面討論的都是左值引用,它引用的是可以取地址的對象(左值):

int x = 42;
int &ref = x;  // 左值引用

2. 右值引用 - 引用臨時對象

C++11引入的右值引用可以綁定到臨時對象(右值):

int &&rref = 42;  // 右值引用綁定到臨時值

右值引用主要用于實(shí)現(xiàn)移動語義和完美轉(zhuǎn)發(fā),這是C++現(xiàn)代高性能編程的基礎(chǔ)。

// 移動構(gòu)造函數(shù)
MyClass(MyClass &&other) {
    // 從other"偷"資源,不需要復(fù)制
}

3. 轉(zhuǎn)發(fā)引用 - 保持值類型的引用

轉(zhuǎn)發(fā)引用(也叫萬能引用)在模板編程中特別有用:

template<typename T>
void func(T &?m) {  // 可能是左值引用也可能是右值引用
    // ...
}

它可以根據(jù)傳入的參數(shù)自動推導(dǎo)為左值引用或右值引用,配合std::forward使用可以完美轉(zhuǎn)發(fā)參數(shù)的值類別。

六、實(shí)戰(zhàn)案例:體驗(yàn)引用的魅力

理論講完了,來點(diǎn)實(shí)際的!讓我們通過幾個實(shí)戰(zhàn)案例,看看引用如何在實(shí)際編程中發(fā)揮作用。

案例一:函數(shù)參數(shù)中的引用 - 讓數(shù)據(jù)"瞬間移動"

// 不用引用的傳統(tǒng)方式
void increaseScore(int *score) {
    if (score != NULL) { // 安全檢查
        (*score) += 10;  // 解引用操作
    }
}

// 使用引用的簡潔方式
void increaseScore(int &score) {
    score += 10;  // 直接用,多簡潔!
}

int main() {
    int playerScore = 50;
    
    // 調(diào)用方式也不同
    increaseScore(&playerScore); // 指針版本
    increaseScore(playerScore);  // 引用版本
}

看到區(qū)別了嗎?用引用時,代碼更加簡潔明了,不需要判斷NULL,不需要加星號解引用,調(diào)用時也不需要加取地址符。

案例二:避免復(fù)制大對象 - 省內(nèi)存高手

假設(shè)我們有個超大的游戲角色類:

class GameCharacter {
private:
    vector<int> healthHistory; // 假設(shè)這里存了成千上萬的歷史數(shù)據(jù)
    string name;
    int level;
    // ... 還有很多很多數(shù)據(jù)
    
public:
    // 構(gòu)造函數(shù)
    GameCharacter(string n) : name(n), level(1) {
        // 初始化大量數(shù)據(jù)
        for (int i = 0; i < 10000; i++) {
            healthHistory.push_back(100);
        }
    }
    
    int getHealth() const {
        return healthHistory.back(); // 訪問healthHistory中的最后一個元素
    }
};

// 不使用引用 - 復(fù)制整個角色(很浪費(fèi)!)
void displayHealth(GameCharacter character) {
    cout << "Health: " << character.getHealth() << endl;
}

// 使用引用 - 只傳遞"別名"(超省內(nèi)存!)
void displayHealth(const GameCharacter &character) {
    cout << "Health: " << character.getHealth() << endl;
}

對于第一個函數(shù),每次調(diào)用都會復(fù)制整個GameCharacter對象,包括那個巨大的healthHistory向量。想象一下,如果角色有10000點(diǎn)歷史健康記錄,那就要復(fù)制10000個整數(shù)!這對內(nèi)存和CPU都是巨大的浪費(fèi)。

而使用引用參數(shù)的第二個函數(shù),只傳遞了一個引用,無需復(fù)制任何數(shù)據(jù)。性能差異可能是幾十倍甚至上百倍!

案例三:引用作為返回值 - 鏈?zhǔn)秸{(diào)用的秘密

class StringBuilder {
private:
    string data;
    
public:
    StringBuilder() : data("") {}
    
    StringBuilder& append(const string &text) {
        data += text;
        return *this;  // 返回自身的引用
    }
    
    StringBuilder& appendLine(const string &text) {
        data += text + "\n";
        return *this;  // 返回自身的引用
    }
    
    string toString() const {
        return data;
    }
};

int main() {
    StringBuilder builder;
    
    // 鏈?zhǔn)秸{(diào)用,優(yōu)雅!
    string result = builder.append("Hello")
                           .append(" ")
                           .append("World")
                           .appendLine("!")
                           .append("Welcome to C++")
                           .toString();
    
    cout << result << endl;
}

通過返回引用,我們可以實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,讓代碼更加優(yōu)雅流暢。這也是很多現(xiàn)代C++庫的常用技巧,如iostream庫的設(shè)計(jì)(cin >>和cout <<)。

案例四:引用做左值 - 修改原始數(shù)據(jù)

class Database {
private:
    vector<int> data;
    
public:
    Database() {
        // 初始化一些數(shù)據(jù)
        for (int i = 0; i < 10; i++) {
            data.push_back(i);
        }
    }
    
    // 返回引用,允許修改
    int& at(int index) {
        return data[index];
    }
    
    // 常量引用,不允許修改
    const int& at(int index) const {
        return data[index];
    }
    
    void printAll() {
        for (int value : data) {
            cout << value << " ";
        }
        cout << endl;
    }
};

int main() {
    Database db;
    
    // 可以作為左值使用
    db.at(3) = 100;
    
    db.printAll(); // 0 1 2 100 4 5 6 7 8 9
}

通過返回引用,at方法的返回值可以作為左值使用,直接修改容器中的元素。如果返回的是值而不是引用,這種寫法是不可能的。

七、引用的陷阱與注意事項(xiàng)

引用功能強(qiáng)大,但也有一些陷阱需要注意:

1. 懸空引用 - 引用了已銷毀的對象

int& getDangerousReference() {
    int local = 42;
    return local;  // 危險!返回了局部變量的引用
}

int main() {
    int &ref = getDangerousReference();  // ref引用了已銷毀的變量
    cout << ref;  // 未定義行為,可能崩潰
}

返回局部變量的引用是非常危險的,因?yàn)榫植孔兞吭诤瘮?shù)結(jié)束后就被銷毀了,引用會變成"懸空引用"。

有趣的是,上面的代碼可能會輸出42,看起來一切正常。這是因?yàn)槟菈K內(nèi)存暫時還沒被覆蓋,值仍然存在。但這完全是偶然的!如果我們稍微修改代碼:

int& getDangerousReference() {
    int local = 42;
    return local;
}

void someOtherFunction() {
    int x = 100;
    int y = 200;
    // 做一些操作
}

int main() {
    int &ref = getDangerousReference();
    someOtherFunction();  // 可能覆蓋之前的棧內(nèi)存
    cout << ref;  // 很可能不再是42
}

調(diào)用someOtherFunction()后,它可能使用相同的棧內(nèi)存,覆蓋原來的42。這就是為什么返回局部變量的引用被視為嚴(yán)重錯誤 - 你永遠(yuǎn)無法預(yù)測它何時會導(dǎo)致程序崩潰。

2. 對臨時對象的引用 - 生命周期陷阱

const string& getName() {
    return "John";  // 返回臨時字符串的引用
}

int main() {
    const string &name = getName();
    cout << name;  // 可能正常工作,但依賴于編譯器實(shí)現(xiàn)
}

這個例子有個大坑!簡單來說:

當(dāng)你在函數(shù)中創(chuàng)建臨時對象(比如這里的字符串"John")并返回它的引用時,就像是把一張即將自毀的紙條的地址給了別人。正常情況下,函數(shù)結(jié)束時這個紙條就"嘭"地消失了。

但 C++ 有個特殊規(guī)則:如果臨時對象被綁定到常量引用(注意必須是const),它的生命周期會被延長。所以上面的代碼可能僥幸能工作。

但這就像走鋼絲一樣危險!稍有不慎(比如忘了const或編譯器實(shí)現(xiàn)不同)就會掉下去。

更安全的做法是直接返回值而不是引用:

string getName() {
    return "John";  // 返回值,讓編譯器處理臨時對象
}

這樣雖然有一次復(fù)制的開銷,但在現(xiàn)代C++中,編譯器通常會使用返回值優(yōu)化(RVO)或移動語義來消除這個開銷。

3. 引用數(shù)組的問題 - C++不支持引用數(shù)組

// 不能創(chuàng)建引用的數(shù)組
// int &refs[10];  // 錯誤!

// 但可以創(chuàng)建數(shù)組的引用
int arr[10] = {0};
int (&ref)[10] = arr;  // ref是對有10個元素的整型數(shù)組的引用

這是 C++ 語法的一個限制,需要特別注意。

八、什么時候用引用,什么時候用指針?

到這里,你可能會問:"既然引用這么好,那我是不是應(yīng)該到處用它?"

不不不,每個工具都有它的適用場景:

用引用的場景:

  • 函數(shù)參數(shù)需要修改原始值
  • 避免復(fù)制大對象(使用const引用)
  • 需要返回函數(shù)內(nèi)部對象的引用(注意不要返回局部變量的引用)
  • 需要鏈?zhǔn)讲僮?/li>
  • 需要作為左值使用返回值
  • 實(shí)現(xiàn)操作符重載

用指針的場景:

  • 對象可能不存在(可能為NULL/nullptr)
  • 需要在運(yùn)行時改變指向的對象
  • 處理動態(tài)分配的內(nèi)存(new/delete)
  • 實(shí)現(xiàn)復(fù)雜的數(shù)據(jù)結(jié)構(gòu)(如鏈表、樹等)
  • 需要指針?biāo)阈g(shù)(如遍歷數(shù)組)
  • 與C語言接口交互

引用和指針各有所長,關(guān)鍵是在正確的場景使用正確的工具。

九、現(xiàn)代C++中的引用最佳實(shí)踐

隨著C++11/14/17/20的發(fā)展,關(guān)于引用的最佳實(shí)踐也在不斷演進(jìn):

1. 優(yōu)先使用常量引用傳遞只讀大型參數(shù)

void process(const BigObject &obj);  // 好
// 而不是
void process(BigObject obj);         // 差 - 會復(fù)制

2. 使用移動語義和右值引用處理臨時對象

class MyString {
public:
    // 移動構(gòu)造函數(shù)
    MyString(MyString &&other) noexcept {
        // 從other"偷"資源,而不是復(fù)制
        data = other.data;
        other.data = nullptr;  // 確保other不再擁有資源
    }
};

右值引用讓我們能夠識別臨時對象,并"偷走"它們的資源而不是復(fù)制,提高了性能。

3. 使用std::reference_wrapper實(shí)現(xiàn)引用容器

C++容器不能直接存儲引用(因?yàn)橐貌荒苤匦沦x值),但可以用std::reference_wrapper解決:

vector<reference_wrapper<int>> refs;
int a = 1, b = 2, c = 3;

refs.push_back(a);
refs.push_back(b);
refs.push_back(c);

refs[0].get() = 100;  // a現(xiàn)在是100

這讓我們能夠在容器中存儲引用,同時保持引用的所有優(yōu)點(diǎn)。

4. 在范圍for循環(huán)中使用引用避免復(fù)制

vector<BigObject> objects;
// ...

// 差 - 每次迭代都復(fù)制對象
for (auto obj : objects) {
    obj.process();
}

// 好 - 使用引用避免復(fù)制
for (auto& obj : objects) {
    obj.process();
}

// 更好 - 如果不修改對象,使用const引用
for (constauto& obj : objects) {
    obj.display();
}

這在處理大型對象集合時尤為重要,可以顯著提高性能。

5. 使用auto&&實(shí)現(xiàn)通用引用轉(zhuǎn)發(fā)

在模板編程中,使用auto&&可以保持值類別:

template<typename Func, typename... Args>
auto invoke_and_log(Func&& func, Args&&... args) {
    cout << "調(diào)用函數(shù)..." << endl;
    return forward<Func>(func)(forward<Args>(args)...);
}

這種技術(shù)在泛型編程中特別有用,可以完美轉(zhuǎn)發(fā)參數(shù)的值類別(左值還是右值)。

6. 使用引用修飾符(ref-qualifiers)區(qū)分對象狀態(tài)

C++11引入了引用修飾符,可以根據(jù)對象是左值還是右值選擇不同的成員函數(shù):

class Widget {
public:
    // 當(dāng)對象是左值時調(diào)用
    void doWork() & { 
        cout << "左值版本" << endl; 
    }
    
    // 當(dāng)對象是右值時調(diào)用
    void doWork() && { 
        cout << "右值版本 - 可以移動內(nèi)部資源" << endl; 
    }
};

Widget makeWidget() { return Widget(); } // 工廠函數(shù)返回臨時對象

int main() {
    Widget w;               // w是一個命名對象(左值)
    w.doWork();             // 調(diào)用左值版本
    
    makeWidget().doWork();  // makeWidget()返回臨時對象(右值),調(diào)用右值版本
}

這讓類能夠根據(jù)對象是臨時的還是持久的來優(yōu)化操作。

7. 優(yōu)先使用視圖(view)而非引用存儲子字符串

C++17引入了string_view,它比字符串引用更靈活:

// 舊方式:使用const string&
void process(const string& str) {
    // 無法直接處理字符串字面量或子字符串
}

// 現(xiàn)代方式:使用string_view
void process(string_view sv) {
    // 可以處理任何類型的字符串,無需復(fù)制
}

// 使用
string s = "Hello World";
process(s);           // 兩種方式都可以
process("Hello");     // string_view可以,const string&需要創(chuàng)建臨時對象
process(s.substr(0, 5)); // string_view不復(fù)制,const string&會復(fù)制

string_view提供了引用語義的所有優(yōu)點(diǎn),但比普通引用更加靈活。

十、總結(jié):引用不只是語法糖,它是一種思維方式

經(jīng)過這一路的探索,我們可以得出結(jié)論:引用確實(shí)在底層可能用指針實(shí)現(xiàn),但它絕不僅僅是指針的語法糖。

它是C++提供的一種更安全、更直觀的編程方式,讓我們能夠:

  • 寫出更簡潔的代碼
  • 避免常見的指針錯誤
  • 表達(dá)更清晰的設(shè)計(jì)意圖
  • 實(shí)現(xiàn)更高效的數(shù)據(jù)傳遞
  • 支持現(xiàn)代C++的移動語義和完美轉(zhuǎn)發(fā)

就像武俠小說里的內(nèi)功心法一樣,掌握了引用的精髓,你的 C++ 代碼將更加簡潔優(yōu)雅,更少Bug,也更容易被他人理解。引用不只是語法層面的東西,它代表了一種對數(shù)據(jù)訪問和修改的思考方式。

下次當(dāng)有人告訴你"引用就是指針的語法糖"時,你可以自信地回答:"才不是呢!它們是兩種不同的編程思維!指針是顯式的間接訪問,而引用是隱式的別名機(jī)制。雖然底層實(shí)現(xiàn)可能相似,但抽象層次和使用哲學(xué)完全不同!"

責(zé)任編輯:趙寧寧 來源: 跟著小康學(xué)編程
相關(guān)推薦

2021-01-06 10:51:39

云計(jì)算云服務(wù)IT

2010-04-08 08:18:55

iPad軟件開發(fā)iPhone

2017-03-25 21:13:38

JavaScript排序

2010-08-05 09:29:08

jQuery

2025-03-28 08:50:00

指針編程C 語言

2015-11-24 10:05:07

私有云虛擬化負(fù)載遷移

2018-03-13 15:00:22

智慧交通高鐵無人駕駛

2013-04-25 13:58:15

編程

2015-02-04 09:45:40

2018-06-28 18:10:41

華為

2016-10-13 18:06:09

云計(jì)算多云模型

2015-12-15 17:19:55

戴爾云計(jì)算

2020-04-26 12:08:10

Python編程語言開發(fā)

2021-11-05 11:17:45

互聯(lián)網(wǎng)996大廠

2018-06-27 17:24:24

華為

2022-11-02 11:48:03

Vanilla OSGNOMEUbuntu

2015-03-31 09:28:28

Hadoop大數(shù)據(jù)技術(shù)大數(shù)據(jù)未來道路

2024-11-26 11:02:17

2020-08-10 10:31:18

物聯(lián)網(wǎng)5G智慧城市

2020-08-26 20:30:07

Android 開源操作系統(tǒng)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 黑人巨大精品 | 成人精品鲁一区一区二区 | 在线播放一区二区三区 | 日韩三区在线 | 草草视频在线观看 | 国产激情偷乱视频一区二区三区 | 国产区第一页 | 色狠狠一区 | 欧美精品一二区 | 激情五月婷婷综合 | 国产专区视频 | a在线视频| 久久精品视频在线免费观看 | 日本精品久久久一区二区三区 | 99这里只有精品视频 | 中文字幕一区二区三区精彩视频 | 久久99精品久久久 | 岛国av一区二区三区 | 亚洲精品三级 | 久久大香 | 午夜影院| 精国产品一区二区三区四季综 | 亚洲一区二区三区四区五区午夜 | 亚洲精品一区av在线播放 | www国产亚洲精品久久网站 | 亚洲欧美日韩在线 | 日韩手机在线视频 | 日本一级淫片免费啪啪3 | 7777精品伊人久久精品影视 | 高清一区二区三区 | www.国产一区| 国产欧美日韩一区 | 久久精品国产一区二区电影 | 国产一区二区在线免费 | 日本一区二区三区免费观看 | 精品国产免费一区二区三区演员表 | 91精品国产777在线观看 | 国产精品久久久亚洲 | 亚洲第一av | 亚洲视频中文字幕 | 国产男女猛烈无遮掩视频免费网站 |