Truyền tham chiếu đến con trỏ trong C ++


130

Theo như tôi có thể nói, không có lý do gì tôi không được phép chuyển một tham chiếu đến một con trỏ trong C ++. Tuy nhiên, những nỗ lực của tôi để làm như vậy là thất bại, và tôi không biết tại sao.

Đây là những gì tôi đang làm:

void myfunc(string*& val)
{
    // Do stuff to the string pointer
}

// sometime later 
{
    // ...
    string s;
    myfunc(&s);
    // ...
}

Và tôi đang nhận được lỗi này:

không thể chuyển đổi tham số 1 từ 'std :: string *' thành 'std :: string * &'

Câu trả lời:


126

Hàm của bạn mong đợi một tham chiếu đến một con trỏ chuỗi thực trong phạm vi gọi, không phải là một con trỏ chuỗi ẩn danh. Như vậy:

string s;
string* _s = &s;
myfunc(_s);

Nên biên dịch tốt.

Tuy nhiên, điều này chỉ hữu ích nếu bạn có ý định sửa đổi con trỏ bạn truyền cho hàm. Nếu bạn có ý định tự sửa đổi chuỗi, bạn nên sử dụng tham chiếu đến chuỗi như Sake đã đề xuất. Với ý nghĩ đó, rõ ràng hơn tại sao trình biên dịch phàn nàn về mã gốc của bạn. Trong mã của bạn, con trỏ được tạo ra 'một cách nhanh chóng', sửa đổi con trỏ đó sẽ không có hậu quả và đó không phải là mục đích. Ý tưởng của một tham chiếu (so với một con trỏ) là một tham chiếu luôn trỏ đến một đối tượng thực tế.


86

Vấn đề là bạn đang cố gắng liên kết tạm thời với tham chiếu, điều mà C ++ không cho phép trừ khi tham chiếu là const.

Vì vậy, bạn có thể làm một trong những điều sau đây:

void myfunc(string*& val)
{
    // Do stuff to the string pointer
}


void myfunc2(string* const& val)
{
    // Do stuff to the string pointer
}

int main()
// sometime later 
{
    // ...
    string s;
    string* ps = &s;

    myfunc( ps);   // OK because ps is not a temporary
    myfunc2( &s);  // OK because the parameter is a const&
    // ...

    return 0;
}

Điều đặc biệt là nên viết loại ra theo cách mà ông Burr làm trong ví dụ số 2 - string* const& valthay vì tương đương ít dễ đọc hơn, ví dụ như const string* &val. Đối với tôi đây rõ ràng là một tham chiếu const , đến một con trỏ, một chuỗi. Cá nhân tôi thích viết T const&thay vì const T&trong các tờ khai để có được sự làm rõ chính xác này.
cá2000

1
Nit pick (không phải là một lời chỉ trích): Đối với tôi, cụm từ bind a temporary to the referencenày rõ ràng hơn trong câu trả lời này so với @Chris 'như reference to an actual string pointer in the calling scope, not an anonymous string pointer. Bất kể, cả hai đều có khả năng đúng theo quan điểm của tôi.
kevinarpe

1
@ fish2000 Không chỉ vậy, const string*&string* const&thực sự là các loại khác nhau. Đầu tiên là một consttham chiếu đến a const string*, trong khi thứ hai là một consttham chiếu đến a string*.
Thời gian của Justin - Phục hồi lại

@ fish2000 hãy cẩn thận của "prefering" T const&để const T&- như @Justin Time chỉ ra, họ là những loại khác nhau. Nó có thể không có hậu quả đôi khi, nhưng trong những tình huống khác, nó sẽ được hoàn toàn quan trọng để thích một hoặc khác, tương tự như thích intđến double.
omatai

3
@ fish2000 - một người nói "đây là tài liệu tham khảo mà bạn có thể thay đổi thành thứ gì đó mà bạn không thể thay đổi" trong khi người khác nói "đây là tài liệu tham khảo bạn không thể thay đổi thành thứ bạn có thể thay đổi". Trong ví dụ này được đưa ra, bạn chỉ đơn giản là không thể trao đổi hai.
omatai

10

Thay đổi nó thành:

  std::string s;
  std::string* pS = &s;
  myfunc(pS);

BIÊN TẬP:

Điều này được gọi ref-to-pointervà bạn không thể chuyển địa chỉ tạm thời làm tham chiếu đến chức năng. (trừ khi nó là const reference).

Mặc dù, tôi đã chỉ ra std::string* pS = &s;(con trỏ tới một biến cục bộ), cách sử dụng thông thường của nó sẽ là: khi bạn muốn callee tự thay đổi con trỏ, chứ không phải đối tượng mà nó trỏ tới. Ví dụ, một hàm phân bổ bộ nhớ và gán địa chỉ của khối bộ nhớ được phân bổ cho đối số của nó phải lấy tham chiếu đến một con trỏ hoặc một con trỏ tới con trỏ:

void myfunc(string*& val)
{
//val is valid even after function call
   val = new std::string("Test");

}

5

&s tạo con trỏ tạm thời cho chuỗi và bạn không thể tạo tham chiếu đến đối tượng tạm thời.


4
Điều này không hoàn toàn đúng - bạn có thể tạo tham chiếu đến const tạm thời
1800 THÔNG TIN

3

Thử:

void myfunc(string& val)
{
    // Do stuff to the string pointer
}

// sometime later 
{
    // ...
    string s;
    myfunc(s);
    // ...
}

hoặc là

void myfunc(string* val)
{
    // Do stuff to the string pointer
}

// sometime later 
{
    // ...
    string s;
    myfunc(&s);
    // ...
}

Đối với những gì tôi đang làm, tôi cần địa chỉ của con trỏ cũng như chính con trỏ. Tôi không muốn vượt qua con trỏ theo giá trị.
Alex

Vẫn không hiểu những gì bạn cố gắng để hoàn thành. Bạn không thể có "địa chỉ của con trỏ" của "chuỗi s", đơn giản vì "chuỗi s" không phải là con trỏ.
Sake

@Alex: Tôi cho rằng bạn cần phát hiện xem chuỗi có hoàn toàn giống với chuỗi khác mà bạn đang lưu trữ hay không và liệu nội dung của chúng có giống nhau hay không. Nếu đó là trường hợp, lưu ý rằng bạn có thể sử dụng địa chỉ của toán tử để tham chiếu và nó sẽ giúp bạn có địa chỉ của đối tượng được tham chiếu: void f (std :: string const & s) {std :: string const * p = & s; }
David Rodríguez - dribeas

3

EDIT: Tôi đã thử nghiệm một số, và phát hiện ra một điều tinh tế hơn tôi nghĩ. Đây là những gì tôi nghĩ bây giờ là một câu trả lời chính xác.

&skhông phải là một giá trị vì vậy bạn không thể tạo tham chiếu đến nó trừ khi loại tham chiếu được tham chiếu đến const. Vì vậy, ví dụ, bạn không thể làm

string * &r = &s;

nhưng bạn có thể làm

string * const &r = &s;

Nếu bạn đặt một khai báo tương tự trong tiêu đề hàm, nó sẽ hoạt động.

void myfunc(string * const &a) { ... }

Có một vấn đề khác, cụ thể là, tạm thời. Quy tắc là bạn chỉ có thể nhận được một tham chiếu đến tạm thời nếu có const. Vì vậy, trong trường hợp này, người ta có thể lập luận rằng & s là tạm thời và do đó phải được khai báo consttrong nguyên mẫu hàm. Từ quan điểm thực tế, nó không có sự khác biệt trong trường hợp này. (Đó là một giá trị hoặc tạm thời. Dù bằng cách nào, quy tắc tương tự cũng được áp dụng.) Tuy nhiên, nói đúng ra, tôi nghĩ rằng đó không phải là tạm thời mà là một giá trị. Tôi tự hỏi nếu có một cách để phân biệt giữa hai. (Có lẽ nó được định nghĩa đơn giản rằng tất cả các thời gian là giá trị và tất cả các giá trị không phải là giá trị tạm thời. Tôi không phải là chuyên gia về tiêu chuẩn.)

Điều đó đang được nói, vấn đề của bạn có thể ở mức cao hơn. Tại sao bạn muốn một tài liệu tham khảo đến địa chỉ của s? Nếu bạn muốn tham chiếu đến một con trỏ tới s, bạn cần xác định một con trỏ như trong

string *p = &s;
myfunc(p);

Nếu bạn muốn một tham chiếu đến shoặc một con trỏ tới s, hãy làm điều đơn giản.


1

Tôi vừa sử dụng một tham chiếu đến một con trỏ để làm cho tất cả các con trỏ trong cây nhị phân bị xóa trừ gốc an toàn. Để làm cho con trỏ an toàn, chúng ta chỉ cần đặt nó thành 0. Tôi không thể tạo hàm xóa cây (chỉ giữ gốc) để chấp nhận một tham chiếu đến một con trỏ vì tôi đang sử dụng gốc (con trỏ này) làm đầu tiên đầu vào để đi qua trái và phải.

void BinTree::safe_tree(BinTree * &vertex ) {
    if ( vertex!=0 ) {  // base case
        safe_tree(vertex->left);    // left subtree.
            safe_tree(vertex->right);   //  right subtree.
          // delete vertex;  // using this delete causes an error, since they were deleted on the fly using inorder_LVR. If inorder_LVR does not perform delete to the nodes, then, use delete vertex;
        vertex=0;  // making a safe pointer
    }
} // end in

Dòng dưới cùng, tham chiếu đến một con trỏ không hợp lệ khi tham số chính thức là con trỏ (this).


1

Chào mừng bạn đến với C ++ 11 và tham chiếu giá trị:

#include <cassert>
#include <string>

using std::string;

void myfunc(string*&& val)
{
    assert(&val);
    assert(val);
    assert(val->c_str());
    // Do stuff to the string pointer
}

// sometime later 
int main () {
    // ...
    string s;
    myfunc(&s);
    // ...
}

Bây giờ bạn có quyền truy cập vào giá trị của con trỏ (được gọi bằng val ), là địa chỉ của chuỗi.

Bạn có thể sửa đổi con trỏ, và không ai quan tâm. Đó là một khía cạnh của những gì một giá trị ở nơi đầu tiên.

Hãy cẩn thận: Giá trị của con trỏ chỉ có giá trị cho đến khi myfunc()trả về. Cuối cùng, nó là tạm thời.


-1

Tôi biết rằng có thể vượt qua các tham chiếu của con trỏ, tôi đã làm nó tuần trước, nhưng tôi không thể nhớ cú pháp là gì, vì mã của bạn có vẻ đúng với bộ não của tôi ngay bây giờ. Tuy nhiên, một lựa chọn khác là sử dụng con trỏ của con trỏ:

Myfunc(String** s)

-8

myfunc ("chuỗi * & val") điều này tự nó không có ý nghĩa gì. "chuỗi * & val" ngụ ý "chuỗi val", * và hủy bỏ lẫn nhau. Cuối cùng, người ta không thể vượt qua biến chuỗi thành một hàm ("chuỗi val"). Chỉ các loại dữ liệu cơ bản có thể được truyền cho một hàm, đối với các loại dữ liệu khác cần phải truyền dưới dạng con trỏ hoặc tham chiếu. Bạn có thể có chuỗi & val hoặc chuỗi * val cho một hàm.


4
Sai trên tất cả các cấp. Vui lòng làm mới cú pháp khai báo var của bạn.
Ari
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.