C ++ tương đương với toString của Java?


151

Tôi muốn kiểm soát những gì được ghi vào một luồng, tức là cout, cho một đối tượng của một lớp tùy chỉnh. Điều đó có khả thi trong C ++ không? Trong Java, bạn có thể ghi đè toString()phương thức cho mục đích tương tự.

Câu trả lời:


176

Trong C ++, bạn có thể quá tải operator<<cho ostreamvà lớp tùy chỉnh của mình:

class A {
public:
  int i;
};

std::ostream& operator<<(std::ostream &strm, const A &a) {
  return strm << "A(" << a.i << ")";
}

Bằng cách này, bạn có thể xuất các thể hiện của lớp trên luồng:

A x = ...;
std::cout << x << std::endl;

Trong trường hợp bạn operator<<muốn in ra nội bộ của lớp Avà thực sự cần quyền truy cập vào các thành viên riêng tư và được bảo vệ, bạn cũng có thể khai báo nó như một chức năng kết bạn:

class A {
private:
  friend std::ostream& operator<<(std::ostream&, const A&);
  int j;
};

std::ostream& operator<<(std::ostream &strm, const A &a) {
  return strm << "A(" << a.j << ")";
}

16
Tốt hơn là khai báo toán tử << là hàm bạn bè của lớp vì nó có thể được yêu cầu để truy cập các thành viên riêng của lớp.
Naveen

5
Tốt hơn hết là khai báo nó như là friend, và bên trong phần thân của lớp - với điều đó, bạn sẽ không phải làm gì using namespacevới không gian tên chứa toán tử (và lớp), nhưng ADL sẽ tìm thấy nó miễn là đối tượng của lớp đó một trong các toán hạng.
Pavel Minaev

... Ở trên có nghĩa là " định nghĩa nó là bạn trong nội bộ của lớp" - như trong định nghĩa thành viên nội tuyến.
Pavel Minaev

2
@fnieto: dumpphương pháp công khai đó là bẩn thỉu và không cần thiết. Sử dụng friendở đây là hoàn toàn tốt. Cho dù bạn thích một phương pháp dư thừa hay xâm nhập friendhoàn toàn là vấn đề của hương vị, mặc dù friendđược cho là đã được giới thiệu cho mục đích chính xác này.
Konrad Rudolph

1
@Pavel: Tra cứu phụ thuộc đối số sẽ tìm thấy nó bằng mọi giá, miễn là toán tử được định nghĩa trong cùng một không gian tên với lớp. Điều này không liên quan gì đến bạn bè và không cần phải khai báo / định nghĩa bên trong lớp. Ngoài ra, làm cho operator<<()hàm thành viên sẽ không hoạt động: bạn sẽ phải biến nó thành hàm thành viên std::ostreamđể chấp nhận toán hạng tay trái std::ostream.
sth

50

Bạn cũng có thể làm theo cách này, cho phép đa hình:

class Base {
public:
   virtual std::ostream& dump(std::ostream& o) const {
      return o << "Base: " << b << "; ";
   }
private:
  int b;
};

class Derived : public Base {
public:
   virtual std::ostream& dump(std::ostream& o) const {
      return o << "Derived: " << d << "; ";
   }
private:
   int d;
}

std::ostream& operator<<(std::ostream& o, const Base& b) { return b.dump(o); }

3
+1 cho chức năng ảo, để sao chép toStringhành vi của Java .
Konrad Rudolph

Tại sao câm thay vì trực tiếp chỉ định toán tử << trong lớp?
tu sĩ

1
vì bạn không muốn có một vòng lặp vô hạn và một vụ tai nạn
fnieto - Fernando Nieto

1
Có lẽ kỹ thuật này là nhanh chóng và dễ dàng để vượt qua các tùy chọn về những gì cần nối tiếp. Nếu không, sẽ cần phải xác định một toán tử kết bạn lớp khác << được khởi tạo với các tùy chọn và dữ liệu để tuần tự hóa.
Samuel Danielson

Một điểm khác là việc triển khai chức năng kết xuất có thể được thực thi bởi một giao diện, điều này không thể thực hiện được khi sử dụng toán tử được đề xuất.
jupp0r

29

Trong C ++ 11, to_opes cuối cùng được thêm vào tiêu chuẩn.

http://en.cppreference.com/w/cpp/opes/basic_opes/to_opes


15
Đây là một bổ sung hữu ích cho trang này, tuy nhiên việc triển khai C ++ khác biệt đáng kể so với trang trong Java / C #. Trong các ngôn ngữ đó, ToString()là một hàm ảo được định nghĩa trên lớp cơ sở của tất cả các đối tượng và do đó được sử dụng như một cách tiêu chuẩn để thể hiện biểu diễn chuỗi của bất kỳ đối tượng nào. Các chức năng trên std::stringchỉ áp dụng cho các loại tích hợp. Cách thành ngữ trong C ++ là ghi đè <<toán tử cho các loại tùy chỉnh.
Drew Noakes

9
"Sự xấu xí" của chữ ký tiêu chuẩn của operator<<, so với Stringngữ nghĩa đơn giản của Java nhắc tôi nhận xét, đó to_string()không chỉ là "một bổ sung hữu ích", mà là cách ưa thích mới để làm điều đó trong C ++. Nếu, như OP, một đại diện chuỗi tùy chỉnh của một lớp Alà mong muốn, chỉ cần viết một string to_string(A a)định nghĩa dưới đây là class Ađủ. Điều này lan truyền với sự kế thừa như trong Java và có thể được kết hợp (bằng cách thêm chuỗi) như trong Java. Non-overriden toString()trong Java dù sao cũng được sử dụng hạn chế.
P Marecki

10

Như một phần mở rộng cho những gì John đã nói, nếu bạn muốn trích xuất biểu diễn chuỗi và lưu trữ nó trong một std::stringđiều này:

#include <sstream>    
// ...
// Suppose a class A
A a;
std::stringstream sstream;
sstream << a;
std::string s = sstream.str(); // or you could use sstream >> s but that would skip out whitespace

std::stringstreamnằm trong <sstream>tiêu đề.


2
Đó là một cách rườm rà vô lý để có được chuỗi nối tiếp!
Gerd Wagner

9

Câu hỏi đã được trả lời. Nhưng tôi muốn thêm một ví dụ cụ thể.

class Point{

public:
      Point(int theX, int theY) :x(theX), y(theY)
      {}
      // Print the object
      friend ostream& operator <<(ostream& outputStream, const Point& p);
private:
      int x;
      int y;
};

ostream& operator <<(ostream& outputStream, const Point& p){
       int posX = p.x;
       int posY = p.y;

       outputStream << "x="<<posX<<","<<"y="<<posY;
      return outputStream;
}

Ví dụ này đòi hỏi sự hiểu biết quá tải toán tử.

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.