почему sizeof std :: string отличается на разных платформах [duplicate]

Что вы можете сделать по этому поводу?

Здесь есть много хороших ответов, объясняющих, что такое пустая ссылка и как ее отладить. Но очень мало о том, как предотвратить проблему или, по крайней мере, сделать ее легче поймать.

Проверить аргументы

Например, методы могут проверять разные аргументы, чтобы увидеть, null и выбросить ArgumentNullException, исключение, явно созданное для этой конкретной цели.

Конструктор для ArgumentNullException даже принимает имя параметра и сообщение как аргументы, чтобы вы могли точно сказать разработчику, что проблема.

public void DoSomething(MyObject obj) {
    if(obj == null) 
    {
        throw new ArgumentNullException("obj", "Need a reference to obj.");
    }
}

Использовать инструменты

Есть также несколько библиотек, которые могут помочь. Например, «Resharper» может предоставить вам предупреждения во время написания кода, особенно если вы используете их атрибут: NotNullAttribute

В разделе «Контракты кода Microsoft» вы используете синтаксис, например Contract.Requires(obj != null), который дает вам проверку выполнения и компиляцию: Представление кодовых контрактов .

Существует также «PostSharp», который позволит вам просто использовать такие атрибуты:

public void DoSometing([NotNull] obj)

Сделав это и сделав PostSharp частью вашего процесса сборки, obj будет проверяться на нуль во время выполнения. См. Ошибка проверки PostSharp

Решение для простого кода

Или вы всегда можете использовать свой собственный подход, используя простой старый код. Например, вот структура, которую вы можете использовать, чтобы поймать нулевые ссылки. Он моделируется после той же концепции, что и Nullable:

[System.Diagnostics.DebuggerNonUserCode]
public struct NotNull where T: class
{
    private T _value;

    public T Value
    {
        get
        {
            if (_value == null)
            {
                throw new Exception("null value not allowed");
            }

            return _value;
        }
        set
        {
            if (value == null)
            {
                throw new Exception("null value not allowed.");
            }

            _value = value;
        }
    }

    public static implicit operator T(NotNull notNullValue)
    {
        return notNullValue.Value;
    }

    public static implicit operator NotNull(T value)
    {
        return new NotNull { Value = value };
    }
}

. Вы использовали бы очень похоже на то, как вы использовали бы Nullable, за исключением цели достижения совершенно противоположного - не допускать null. Вот несколько примеров:

NotNull person = null; // throws exception
NotNull person = new Person(); // OK
NotNull person = GetPerson(); // throws exception if GetPerson() returns null

NotNull неявно отбрасывается в и из T, поэтому вы можете использовать его в любом месте, где это необходимо. Например, вы можете передать объект Person методу, который принимает значение NotNull:

Person person = new Person { Name = "John" };
WriteName(person);

public static void WriteName(NotNull person)
{
    Console.WriteLine(person.Value.Name);
}

Как вы можете видеть выше, как с помощью nullable, вы получите доступ к базовому значению через Value имущество. Кроме того, вы можете использовать явный или неявный листинг, вы можете увидеть пример с возвращаемым значением ниже:

Person person = GetPerson();

public static NotNull GetPerson()
{
    return new Person { Name = "John" };
}

Или вы даже можете использовать его, когда метод просто возвращает T (в этом случае Person), выполнив бросок. Например, следующий код будет похож на код выше:

Person person = (NotNull)GetPerson();

public static Person GetPerson()
{
    return new Person { Name = "John" };
}

Объединить с Extension

Объединить NotNull с методом расширения, и вы можете охватить еще больше ситуаций. Вот пример того, как может выглядеть метод расширения:

[System.Diagnostics.DebuggerNonUserCode]
public static class NotNullExtension
{
    public static T NotNull(this T @this) where T: class
    {
        if (@this == null)
        {
            throw new Exception("null value not allowed");
        }

        return @this;
    }
}

И вот пример того, как он может быть использован:

var person = GetPerson().NotNull();

GitHub

Для вашей справки я сделал код выше, доступный на GitHub, вы можете найти его по адресу:

https://github.com/luisperezphd/NotNull

Функция родственного языка

В C # 6.0 был введен «оператор с нулевым условием», который немного помогает в этом. С помощью этой функции вы можете ссылаться на вложенные объекты, и если какой-либо из них null, все выражение возвращает null.

Это уменьшает количество нулевых проверок, которые вы должны выполнять в некоторых случаях. Синтаксис заключается в том, чтобы поставить вопросительный знак перед каждой точкой. Возьмите следующий код, например:

var address = country?.State?.County?.City;

Представьте, что country является объектом типа Country, который имеет свойство, называемое State и т. Д. Если country, State, County или City - null, то address will be null . Therefore you only have to check whether адрес is null`.

Это отличная функция, но она дает вам меньше информации. Это не делает очевидным, какой из 4 является нулевым.

Встроенный как Nullable?

C # имеет красивую стенографию для Nullable, вы можете сделать что-то нулевое помещая знак вопроса после такого типа int?.

Было бы неплохо, если бы у C # было что-то вроде структуры NotNull выше и имела аналогичную стенографию, может быть, восклицательный знак (!), чтобы вы могли написать что-то вроде: public void WriteName(Person! person).

15
задан agam 22 September 2010 в 16:19
поделиться

6 ответов

Некоторые реализации std::string сохраняют очень маленькие строки непосредственно в стеке в массиве статического размера char вместо использования динамического хранилища кучи. Это позволяет избежать выделения кучи для множества небольших строковых объектов и улучшает локальность ссылки.

Кроме того, будет существовать член std::size_t, чтобы сохранить размер строк и (потенциально неиспользуемый, см. Выше) указатель к хранилищу кучи.

41
ответ дан Bill 3 September 2018 в 17:00
поделиться

Он зависит от библиотеки. Вы не должны полагаться на размер объектов std::string, потому что это может измениться в разных средах (очевидно, между разными поставщиками стандартной библиотеки, но также между разными версиями одной и той же библиотеки).

Хранить в что реализация std::string написана людьми, которые оптимизированы для различных вариантов использования, что обычно приводит к двум внутренним представлениям: одному для коротких строк (небольшой внутренний буфер) и одному для длинных строк (выделенному кучей внешнему буферу). Накладные расходы связаны с удержанием обоих из них внутри каждого объекта std::string.

3
ответ дан André Caron 3 September 2018 в 17:00
поделиться

std::string обычно содержит буфер для «оптимизации небольших строк» ​​--- если строка меньше размера буфера, тогда не требуется выделение кучи.

10
ответ дан Anthony Williams 3 September 2018 в 17:00
поделиться

Q: Почему собака желтая? A: Это не обязательно.

Размер объекта (a?) Std :: string зависит от реализации. Я просто проверил MS VC ++ 2010. Он действительно использует 32 байта для std :: string. Существует 16-байтовый союз, который содержит либо текст строки, если он подходит, либо указатель на хранилище кучи для более длинных строк. Если разработчики решили сохранить 18 байтовых строк в строковом объекте, а не в куче, размер будет 34 байта. Остальные 16 байтов содержат служебные данные, содержащие такие вещи, как длина строки и объем памяти, выделенной в настоящее время для строки.

Другая реализация всегда может выделять память из кучи. Такая реализация, несомненно, потребует меньше памяти для строкового объекта.

1
ответ дан Jive Dadson 3 September 2018 в 17:00
поделиться

Мое предположение:

class vector
{
    char type;
    struct Heap
    {
      char*   start;
      char*   end;
      char*   allocatedEnd;
    };
    struct Stack
    {
      char    size;
      char    data[27];
    }
    union
    {
        Stack   stackVersion;
        Heap    heapVersion;
    } version;
};

Но я уверен, что есть сотни способов сделать это.

4
ответ дан Martin York 3 September 2018 в 17:00
поделиться

В g ++ 5.2 (например, g ++ 4.9 это отличается) строка в основном определяется как:

class string {
  char* bufferp;
  size_t length;
  union {
    char local_buffer[16];
    size_t capacity;
  };
};

На обычном компьютере это добавляет до 32 байтов (8+ 8 + 16).

Фактическое определение, конечно,

typedef basic_string<char> string;

, но идея одинаков.

2
ответ дан n.caillou 3 September 2018 в 17:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: