Может ли конструктор класса C++ знать имя своего экземпляра?

Можно ли узнать имя экземпляра объекта/имя переменной из метода класса? Например:

#include <iostream>

using namespace std;

class Foo {
     public:
          void Print();
};

void Foo::Print() {
     // what should be ????????? below ?
     // cout << "Instance name = " << ?????????;
}

int main() {
    Foo a, b;
    a.Print();
    b.Print();
    return 0;
}

person Adam Dempsey    schedule 25.11.2009    source источник
comment
любопытно, если это только для целей отладки/регистрации? я надеюсь, что это так   -  person Simon_Weaver    schedule 25.11.2009
comment
Да, просто задавался вопросом, можно ли помочь сузить проблему, с которой я столкнулся.   -  person Adam Dempsey    schedule 26.11.2009
comment
Непонятно, что вы подразумеваете под именем экземпляра. Давайте рассмотрим Foo bar[200]. Какое имя экземпляра должно быть для bar[13]? Или даже прозаично, пусть new Foo() вернул 0xbadbad указатель. Какое имя экземпляра для *(0xbadbad)?   -  person ivaigult    schedule 24.08.2015
comment
Возможно, это поможет: llvm.org/docs/ В clang вы можете подумать о доступе к некоторой отладочной информации - но, пожалуйста, не задавайте мне дальнейших вопросов по этому поводу - я никогда не использую ее и не планирую использовать в будущем...   -  person PiotrNycz    schedule 24.08.2015
comment
вы могли бы испустить хорошую форму этого. т.е. адрес объекта   -  person pm100    schedule 26.08.2015


Ответы (13)


Не с самим языком, но вы можете написать что-то вроде:

#include <iostream>
#include <string>

class Foo
{
 public:
    Foo(const std::string& name) { m_name = name;}
    void Print() { std::cout << "Instance name = " << m_name << std::endl; }

  private:
    std::string m_name;
};

int main() 
{
    Foo a("a");
    Foo b("b");

    a.Print();
    b.Print();

    return 0;
}
person Steven Keith    schedule 25.11.2009

Нет. Имена переменных для программиста, компилятор видит адреса.

Другие языки, которые предоставляют метаданные/отражение своей программы, могут предоставлять эту функциональность, C++ не является одним из этих языков.

person GManNickG    schedule 25.11.2009
comment
Соглашаться. Вопрос в его нынешнем виде не имеет смысла. - person Marco A.; 22.08.2015

Имена переменных не существуют в скомпилированном коде.

Однако вы можете использовать некоторые #define, чтобы получить имя в предварительной обработке и позволить именам быть заполненными до компиляции.

Что-то вроде этого:

#define SHOW(a) std::cout << #a << ": " << (a) << std::endl
// ...
int i = 2;
SHOW (i);
person Svetlozar Angelov    schedule 25.11.2009
comment
Variable names do not exist in the compiled code. Верно ли это даже для двоичных файлов с символами отладки. - person TheMeaningfulEngineer; 21.08.2015

Что бы это значило?

void f(T const& p) {
    cout << p.name();
}

T r() {
    T c;
    return c;
}

void g() {
    T a;
    cout << a.name();
    T & b = a;
    cout << b.name();
    T * ptr = &b; 
    cout << ptr->name();

    T d = r();
    cout << d.name();
}

Что вы ожидаете? "а" каждый раз? А как же к/д?

person Luc Hermitte    schedule 25.11.2009

Имена переменных не сохраняются при компиляции. Лучшее, что вы можете сделать, это передать имя переменной в конструктор объекта и сохранить его внутри объекта с помощью макроса. Последнее приведет к действительно уродливому коду, поэтому вам нужно это только в крайнем случае.

person sharptooth    schedule 25.11.2009

За награду: это один из самых больших и отвратительных хаков, которые я когда-либо создавал, но, на мой взгляд, он достаточно хорош для отладки.

#include <iostream>

#include <typeinfo>
#define DEBUG_INSTANCE( classtype, name ) class _ ## classtype ## _INSTANCE_ ## name ## _  : public classtype \
    { \
        public: \
            _ ## classtype ## _INSTANCE_ ## name ## _ (){ } \
    }; \
_ ## classtype ## _INSTANCE_ ## name ## _ name

class Foo {
public:
    virtual void _MakeTypeIDRunTime() { }
    // A virtual method in the class forces typeid(*this) to be used runtime rather than compiled
    // See: https://stackoverflow.com/a/6747130/1924602

    void Print();
};

void Foo::Print() {
    std::cout << "Instance name = " << typeid(*this).name() << std::endl;
}

int main()
{
    DEBUG_INSTANCE(Foo, a);
    DEBUG_INSTANCE(Foo, b);

    a.Print();
    b.Print();

    system("PAUSE");
    return 0;
}

Вывод:

Instance name = ?AV_Foo_INSTANCE_a_@?1?main@
Instance name = ?AV_Foo_INSTANCE_b_@?1?main@
Press any key to continue . . .

Этот макрос создаст класс, который наследует Foo, а имя содержит имя экземпляра. Единственным ограничением является то, что Foo имеет конструктор по умолчанию и должен содержать виртуальный метод, чтобы typeid мог принимать унаследованные классы и вызываться во время выполнения. См. https://stackoverflow.com/a/6747130/1924602 для лучшего объяснения.

Может быть возможно поддерживать конструкторы, если вы используете макрос __VA_ARGS__

person otc    schedule 22.08.2015

Конечно, экземпляр может узнать свое имя из метода класса:

#include <iostream>

class Foo {
  public:
    void Print() { std::cout << "Instance name = " << this << std::endl; }
};

int main() {
    Foo a, b;
    a.Print();
    b.Print();
    return 0;
}

будет производить вывод, подобный этому:

Instance name = 0x7fff502b8b48
Instance name = 0x7fff502b8b40

Что касается знания имени переменной, то это, конечно, невозможно. Существование объекта не означает существование переменной - этот экземпляр:

new Foo();

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

person Leon    schedule 25.08.2015
comment
Я не согласен, что вы можете называть this именем (учитывая, что это адрес объекта), но можете сойти за uniqe identifier. Наградит вас, если вы добавите пример того, как получить имена переменных из адресов памяти из двоичного файла, созданного отладкой. Не во время выполнения, а как отдельный шаг (путем поиска двоичного файла или чего-то еще). Пожалуйста, пусть пример включает команду компиляции со всеми необходимыми флагами. - person TheMeaningfulEngineer; 26.08.2015
comment
Ну, это вопрос определения. Что касается остальной части вашего комментария, то и a, и b являются автоматическими переменными, поэтому их не будет в таблицах символов в двоичных файлах. Единственный раз, когда можно сопоставить адрес (указатель this) с переменной, — это проверка во время выполнения предыдущего кадра стека и сопоставление его содержимого с отладочной информацией. В основном то, что делает отладчик, только внутри процесса. Не невозможно, но потребовался бы узкоспециализированный и непереносимый фрагмент кода, который практически не использовался бы в реальной жизни. - person Leon; 30.08.2015

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

Я повторно использую пример Стивена Кейта в сочетании с препроцессором #define:

#include <iostream>
#include <string>

using namespace std;

class Foo
{
 public:
    Foo(const string& name) : m_name(name) {}
    void Print() { cout << "Instance name = " << m_name << endl; }

 private:
    string m_name;
};

#define DRESSED_Foo(var) Foo var(#var)

int main() 
{
    DRESSED_Foo(a);
    DRESSED_Foo(b);

    a.Print();
    b.Print();

    return 0;
}
person blashser    schedule 23.08.2015

Ключевое слово это

Я новичок в программировании, но изучив структуру классов, я полагаю, что вы, возможно, ищете ключевое слово this. Как и в приведенном ниже примере (взятом с cplusplus.com), вы можете видеть, что this используется везде, где класс должен ссылаться на себя.

Следовательно, это может сделать и конструктор.

// example on this
#include <iostream>
using namespace std;

class Dummy {
  public:
    bool isitme (Dummy& param);
};

bool Dummy::isitme (Dummy& param)
{
  if (&param == this) return true;
  else return false;
}

int main () {
  Dummy a;
  Dummy* b = &a;
  if ( b->isitme(a) )
    cout << "yes, &a is b\n";
  return 0;
}

http://www.cplusplus.com/doc/tutorial/templates/

person ganrob    schedule 26.08.2015

Это невозможно. C++ не имеет понятия "отражение", как платформа .NET. Но в библиотеке MFC есть класс CRunTime - можете посмотреть, например.

person Captain Comic    schedule 25.11.2009
comment
даже с отражением вы не можете видеть имена локальных переменных - только свойства, методы и поля - person Simon_Weaver; 25.11.2009

Символы отладки не существуют в C++. В результате никакой механизм C++ не позволяет вам что-либо с ними делать. Можно ли создать решение для конкретной платформы, которое будет работать? Возможный. Придется проанализировать образ процесса в памяти, разобрать его по определенному формату, вычислить отладочные символы (и фреймы) и сопоставить их с адресами. По сути, отлаживать себя. Стоит ли заморачиваться? Не по моему мнению.

person SergeyA    schedule 21.08.2015

Вы можете сделать это с помощью #define. Определите макрос, который будет сохранять имя переменной внутри класса:

#include <iostream>
#include <string>

using namespace std;

#define CREATE_FOO(f) Foo f = Foo(#f);

class Foo {
     public:
          void Print() const;
          Foo(string s): name(s) {};
     protected:
          string name;

};

void Foo::Print() const  {
     cout << "Instance name = " << name;
}


int main() {
    CREATE_FOO(a);
    CREATE_FOO(b);
    a.Print();
    b.Print();
    return 0;
}
person EvgeniyZh    schedule 27.08.2015

person    schedule
comment
Не могли бы вы немного отформатировать свой ответ, возможно, это именно то, что я искал. - person TheMeaningfulEngineer; 27.08.2015
comment
только что отформатированный, в основном я думаю, что вы запутались в функции печати и команде печати, которую я использовал в gdb, поэтому просто отлаживайте ее и используйте p this и pt .. - person Naumann; 27.08.2015
comment
Я стремился отформатировать ответ визуально и не иметь ненужных комментариев в коде. После перепроверки я, похоже, ошибся в том, что это решение, которое я искал. Ни в коем случае в gdb фактическое имя переменной фактически не печатается, и печать this для идентификации переменной может быть выполнена без каких-либо отладочных символов. - person TheMeaningfulEngineer; 27.08.2015
comment
да, именно поэтому я заявил, что решения нет, и в функции печати я напечатал это и т. д. - person Naumann; 27.08.2015