==
тесты для ссылочного равенства (независимо от того, являются ли они одним и тем же объектом).
.equals()
тесты для равенства значений (независимо от того, являются ли они логически «равными»).
Objects.equals () проверяет наличие null
перед вызовом .equals()
, поэтому вам не нужно (доступно с JDK7, также доступным в Guava ).
String.contentEquals () сравнивает содержимое String
с содержимым любого CharSequence
(доступно с Java 1.5).
Следовательно, если вы хотите проверить, имеет ли две строки одно и то же значение, вы, вероятно, захотите использовать Objects.equals()
.
// These two have the same value
new String("test").equals("test") // --> true
// ... but they are not the same object
new String("test") == "test" // --> false
// ... neither are these
new String("test") == new String("test") // --> false
// ... but these are because literals are interned by
// the compiler and thus refer to the same object
"test" == "test" // --> true
// ... string literals are concatenated by the compiler
// and the results are interned.
"test" == "te" + "st" // --> true
// ... but you should really just call Objects.equals()
Objects.equals("test", new String("test")) // --> true
Objects.equals(null, "test") // --> false
Objects.equals(null, null) // --> true
Вы почти всегда хотите использовать Objects.equals()
. В редкой ситуации, когда вы знаете, что имеете дело с интернированными строками, вы можете использовать ==
.
Из JLS 3.10. 5. Строковые литералы :
Кроме того, строковый литерал всегда ссылается на тот же экземпляр класса
blockquote>String
. Это связано с тем, что строковые литералы, или, в более общем смысле, строки, которые являются значениями константных выражений ( §15.28 ), «интернированы», чтобы обмениваться уникальными экземплярами, используя методString.intern
.. Подобные примеры также можно найти в JLS 3.10.5-1 .
В документации Boost.Asio говорится:
Объект буфера представляет собой непрерывную область памяти в виде кортежа, состоящего из указателя и размера в байтах. Кортеж вида {void*, size_t} указывает на изменяемую (модифицируемую) область памяти.
Это означает, что для того, чтобы вызов async_read
записал данные в буфер, он должен быть (в базовом объекте буфера) непрерывным блоком памяти. Кроме того, объект буфера должен иметь возможность записи в этот блок памяти.
std::string
не позволяет произвольную запись в свой буфер, поэтому async_read
не может записывать куски памяти в буфер строки (обратите внимание, что std:: string
does предоставляет вызывающей стороне доступ только для чтения к базовому буферу через метод data()
, который гарантирует, что возвращаемый указатель будет действителен до следующего вызова неконстантной функции-члена. По этой причине Asio может легко создать const_buffer
, обернув std::string
, и вы можете использовать его с async_write
).
В документации Asio есть пример кода для простой программы "чат" (см. http://www.boost.org/doc/libs/1_43_0/doc/html/boost_asio/examples.html#boost_asio.examples.chat), в котором есть хороший метод преодоления этой проблемы. По сути, вам нужно, чтобы отправляющий TCP сначала передал размер сообщения в своеобразном "заголовке", а ваш обработчик чтения должен интерпретировать заголовок, чтобы выделить буфер фиксированного размера, подходящий для чтения фактических данных.
Что касается необходимости использования shared_from_this()
в async_read
и async_write
, то причина в том, что это гарантирует, что метод, обернутый boost::bind
, всегда будет ссылаться на живой объект. Рассмотрим следующую ситуацию:
handle_accept
вызывает async_read
и отправляет обработчик "в реактор" - по сути, вы попросили io_service
вызвать Connection::handle_user_read
, когда он закончит чтение данных из сокета. io_service
сохраняет этот функтор и продолжает свой цикл, ожидая завершения операции асинхронного чтения. async_read
объект Connection
по какой-то причине (завершение программы, состояние ошибки и т.д.) деаллоцируется. )io_service
теперь определяет, что асинхронное чтение завершено, после того, как Connection
объект был деаллоцирован, но до того, как io_service
был уничтожен (это может произойти, например, если io_service: :run
выполняется в отдельном потоке, что является типичным). Теперь io_service
пытается вызвать обработчик, и у него есть недопустимая ссылка на объект Connection
. Решением является выделение Connection
через shared_ptr
и использование shared_from_this()
вместо this
при отправке обработчика "в реактор". это позволяет io_service
хранить общую ссылку на объект, а shared_ptr
гарантирует, что он не будет деаллоцирован, пока не истечет срок действия последней ссылки.
Итак, ваш код должен выглядеть примерно так:
class Connection : public boost::enable_shared_from_this<Connection>
{
public:
Connection(tcp::acceptor &acceptor) :
acceptor_(acceptor),
socket_(acceptor.get_io_service(), tcp::v4())
{ }
void start()
{
acceptor_.get_io_service().post(
boost::bind(&Connection::start_accept, shared_from_this()));
}
private:
void start_accept()
{
acceptor_.async_accept(socket_,
boost::bind(&Connection::handle_accept, shared_from_this(),
placeholders::error));
}
void handle_accept(const boost::system::error_code& err)
{
if (err)
{
disconnect();
}
else
{
async_read(socket_, boost::asio::buffer(user_),
boost::bind(&Connection::handle_user_read, shared_from_this(),
placeholders::error, placeholders::bytes_transferred));
}
}
//...
};
Обратите внимание, что теперь вы должны убедиться, что каждый объект Connection
выделен через shared_ptr
, например:
boost::shared_ptr<Connection> new_conn(new Connection(...));
Надеюсь, это поможет!