Кэширование символа константы * как тип возврата



--testdata-begin
if not object_id(N'Tempdb..#T') is null
    drop table #T
Go
Create table #T([id] int,[Question] nvarchar(23))
Insert #T
select 13,N'ABC' union all
select 13,N'ABC' union all
select 13,N'QWE' union all
select 13,N'ABC' union all
select 13,N'QWE' union all
select 13,N'ABC'
Go
--testdata-end
WITH cte AS  (
Select id,Question,COUNT(1) AS num from #T GROUP BY id,Question
)
SELECT id,
       STUFF(
       (
           SELECT ',' + RTRIM(b.num) + ' ' + b.Question
           FROM cte b
           WHERE a.id = b.id
           FOR XML PATH('')
       ),
       1,
       1,
       ''
            ) AS Result
FROM cte a
GROUP BY id;
5
задан Sean Allred 4 July 2012 в 07:18
поделиться

12 ответов

Как насчет этого:

const char *getHelloString() const
{
    return "HelloWorld!";
}

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

23
ответ дан 18 December 2019 в 07:11
поделиться

Хорошо Ну и дела, если мы говорим о просто функции, что Вы всегда хотите возвратить то же значение. это довольно просто.

const char * foo() 
{
   static char[] return_val= "HelloWorld!";
   return return_val;
}

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

const char * foo() 
{
   static std::string output;
   DoCalculation(output);
   return output.c_str();
}

также функциональная подпись

const char *getHelloString() const;

только применимо для функций членства. В которой точке Вы не должны иметь дело со статическими функциональными локальными переменными и могли просто использовать членскую переменную.

2
ответ дан 18 December 2019 в 07:11
поделиться

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

Если Вам любопытно на предмет как Visual C++ type_info::name() реализация выделяет и кэширует свою память, не трудно узнать. Во-первых, создайте крошечную тестовую программу:

#include <cstdio>
#include <typeinfo>
#include <vector>    
int main(int argc, char* argv[]) {
    std::vector<int> v;
    const type_info& ti = typeid(v);
    const char* n = ti.name();
    printf("%s\n", n);
    return 0;
}

Создайте его и выполните его под отладчиком (я использовал WinDbg), и посмотрите на указатель, возвращенный type_info::name(). Это указывает на глобальную структуру? Если так, WinDbg ln команда скажет название самого близкого символа:

0:000> ?? n
char * 0x00000000`00857290
 "class std::vector<int,class std::allocator<int> >"
0:000> ln 0x00000000`00857290
0:000>

ln не распечатал ничего, которое указывает, что строка не была в диапазоне адресов, принадлежавших никакому определенному модулю. Это было бы в том диапазоне, если бы это было в данных или сегменте данных только для чтения. Давайте посмотрим, было ли это выделено на "куче" путем поиска всей "кучи" адрес, возвращенный type_info::name():

0:000> !heap -x 0x00000000`00857290
Entry             User              Heap              Segment               Size  PrevSize  Unused    Flags
-------------------------------------------------------------------------------------------------------------
0000000000857280  0000000000857290  0000000000850000  0000000000850000        70        40        3e  busy extra fill 

Да, это было выделено на "куче". Помещение точки останова в начале malloc() и перезапуск программы подтверждает это.

Рассмотрение объявления в <typeinfo> дает ключ к разгадке то, где указатели "кучи" становятся кэшируемыми:

struct __type_info_node {
    void *memPtr;
    __type_info_node* next;
};

extern __type_info_node __type_info_root_node;
...
_CRTIMP_PURE const char* __CLR_OR_THIS_CALL name(__type_info_node* __ptype_info_node = &__type_info_root_node) const;

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

Страница MSDN, связанная в исходном вопросе, кажется, восполняет пробелы: имя хранится в его украшенной форме для оставления свободного места, и эта форма доступна через type_info::raw_name(). Когда Вы звоните type_info::name() впервые на данном типе, это не украшает имя, хранит его в выделенном "куче" буфере, кэширует буферный указатель и возвращает его.

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

3
ответ дан 18 December 2019 в 07:11
поделиться

Я думаю, что, так как они знают, что существует конечное число их, они просто имеют в наличии их навсегда. Для Вас могло бы быть уместно сделать это в некоторых случаях, но как правило, станд.:: строка будет лучше.

Они могут также искать новые вызовы, чтобы видеть, сделали ли они уже ту строку, и возвратите тот же указатель. Снова, в зависимости от того, что Вы делаете, это может быть полезно для Вас также.

1
ответ дан 18 December 2019 в 07:11
поделиться

Будьте осторожны при реализации функции, которая выделяет блок памяти и затем ожидает, что вызывающая сторона освободит ее, как Вы делаете в OP:

const char *getHelloString() const
{
  char *returnVal = new char[13];
  strcpy("HelloWorld!", returnVal);

  return returnVal
}

Путем выполнения этого Вы передаете владение памяти вызывающей стороне. Если Вы называете этот код от некоторой другой функции:

int main()
{
  char * str = getHelloString();
  delete str;
  return 0;
}

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

Кроме того, по крайней мере, в соответствии с Windows, если две функции находятся в 2 различных модулях, Вы могли бы потенциально повредить "кучу". В частности, если основной () будет в hello.exe, скомпилированном в VC9, и getHelloString () находится в utility.dll, скомпилированном в VC6, то Вы повредите "кучу" при удалении памяти. Это вызвано тем, что VC6 и VC9 и используют их собственную "кучу", и они не та же "куча", таким образом, Вы выделяете от одной "кучи" и освобождаете от другого.

1
ответ дан 18 December 2019 в 07:11
поделиться

Почему возврат вводит потребность быть const? Не думайте о методе как о получить методе, думайте о нем как о создать методе. Я видел много API, который требует, чтобы Вы удалили что-то, что оператор/метод создания возвращает. Просто удостоверьтесь, что Вы отмечаете это в документации.

/* create a hello string
 * must be deleted after use
 */
char *createHelloString() const
{
  char *returnVal = new char[13];
  strcpy("HelloWorld!", returnVal);

  return returnVal
}
0
ответ дан 18 December 2019 в 07:11
поделиться

То, что я часто делал, когда мне нужен этот вид функциональности, должно иметь символ * указатель в классе - инициализированный к пустому указателю - и выделить при необходимости.

то есть:

class CacheNameString
{
    private: 
        char *name;
    public:
        CacheNameString():name(NULL)  { }

    const char *make_name(const char *v)
    {
        if (name != NULL)
            free(name);

        name = strdup(v);

        return name;
    }

};
0
ответ дан 18 December 2019 в 07:11
поделиться

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

В случае std::type_info класс, это - часть стандарта C++, поскольку его пространство имен подразумевает. Память, возвращенная из name() на самом деле указан на статическое ЗУ, созданное компилятором и компоновщиком, когда класс был скомпилирован и является частью системы идентификации типа выполнения (RTTI). Поскольку это относится к символу в пространстве кода, Вы не должны пытаться удалить его.

0
ответ дан 18 December 2019 в 07:11
поделиться

Что-то вроде этого сделало бы:

const char *myfunction() {
    static char *str = NULL; /* this only happens once */
    delete [] str; /* delete previous cached version */
    str = new char[strlen("whatever") + 1]; /* allocate space for the string and it's NUL terminator */
    strcpy(str, "whatever");
    return str;
}

Править: Что-то, что произошло со мной, - то, что хорошая замена для этого могла возвращать повышение:: shared_pointer вместо этого. Тем путем вызывающая сторона может держать на него, пока они хотят, и они не должны волноваться о явном удалении его. Справедливый компромисс IMO.

0
ответ дан 18 December 2019 в 07:11
поделиться

Это, вероятно, сделало использование статического буфера:

const char* GetHelloString()
{
    static char buffer[256] = { 0 };
    strcpy( buffer, "Hello World!" );
    return buffer;
}

Этот буфер похож на глобальную переменную, которая доступна только от этой функции.

-3
ответ дан 18 December 2019 в 07:11
поделиться

Вы не можете полагаться на GC; это - C++. Это означает, что необходимо сохранить память доступной, пока программа не завершается. Вы просто не знаете, когда становится безопасно удалить [] его. Так, если Вы хотите создать и возвратить символ константы*, простой новый [] это и возвратить его. Примите неизбежную утечку.

-5
ответ дан 18 December 2019 в 07:11
поделиться

Я думаю, что что-то вроде этого может быть реализовано только «чисто», используя объекты и Raii Idiom. Когда вызывается деструктор объектов (OBJ выходит из спецификации), мы можем безопасно предположить, что указатели Const Char * больше не используются.

Пример кода:

class ICanReturnConstChars
{
    std::stack<char*> cached_strings
    public:
    const char* yeahGiveItToMe(){
        char* newmem = new char[something];
        //write something to newmem
        cached_strings.push_back(newmem);
        return newmem;
    }
    ~ICanReturnConstChars(){
        while(!cached_strings.empty()){
            delete [] cached_strings.back()
            cached_strings.pop_back()
        }
    }
};

Единственная другая возможность, которую я знаю, это пройти Smart_ptr ..

0
ответ дан 18 December 2019 в 07:11
поделиться
Другие вопросы по тегам:

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