Здесь есть другой угол, которого я не видел в других ответах.
В C ++ способ создания массива-подобного класса заключается в перегрузке []
(известный как оператор подписки):
std::string operator[](int nIndex);
Но это определение позволяет только читать элементы.Чтобы поддержать написание, вам нужно вернуть то, что мы, фанаты C ++, вскоре должны начать называть lvalue reference
:
std::string& operator[](int nIndex);
Это означает (по крайней мере, в этом контексте), что оно может появиться в левой частивыражение присваивания:
list[3] = "hi";
Но это поднимает все виды страшных проблем времени жизни.Если вы можете получить такую ссылку, вы можете привязать ее к имени:
std::string& oops = list[3];
Теперь oops
относится к чему-то во внутренних органах list
.Это просто не очень безопасная ситуация, потому что некоторые операции с list
приведут к тому, что oops
станет бомбой замедленного действия, и вам нужно иметь подробную спецификацию того, как list
работает, чтобы избежать этого.
C # работает по-другому.Разработчик класса, похожего на массив, получает возможность определить два отдельных определения для оператора []
как часть indexer (имя C # для реализации оператора индекса):
public string this[int index]
{
get { /* return a string somehow */ }
set { /* do something with implicit 'value' param */ }
}
Это аккуратно обходит необходимость в ссылках на lvalue.Это также означает, что ваш простой пример, который работает на самом деле, требует некоторой изящной работы компилятора:
ms[0] += addie;
Как уже говорилось во многих других ответах, это реализовано с использованием обычного оператора +
, поэтому он расширяется до:
ms[0] = ms[0] + addie;
Но эти два вхождения ms[0]
на самом деле являются вызовами совершенно разных методов.Это немного похоже на:
ms.SetAt(0, ms.GetAt(0) + addie);
Вот почему этот пример работает.Он заменяет объект, хранящийся в списке.Это тот шаг, который отсутствует в вашем нерабочем примере.