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

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

#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;
}

Ответы

Ответ 1

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

#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;
}

Ответ 2

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

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

Ответ 3

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

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

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

#define SHOW(a) std::cout << #a << ": " << (a) << std::endl
// ...
int i = 2;
SHOW (i);

Ответ 4

Что это значит?

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();
}

Чего вы ожидаете? "a" каждый раз? А как насчет c/d?

Ответ 5

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

Ответ 6

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

#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: /questions/338270/programatically-getting-the-name-of-a-derived-class/1606164#1606164

    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 = [email protected][email protected]
Instance name = [email protected][email protected]
Press any key to continue . . .

Этот макрос создаст класс, который наследует Foo, а имя содержит имя экземпляра. Единственное ограничение состоит в том, что Foo имеет конструктор по умолчанию и должен содержать виртуальный метод, чтобы typeid принимал унаследованные классы и назывался используемым во время выполнения. Подробнее см. fooobar.com/questions/338270/....

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

Ответ 7

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

#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();

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

Ответ 8

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

Я повторно использую пример Стивена Кита в сочетании с препроцессором #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;
}

Ответ 9

Ключевое слово this

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

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

// 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/

Ответ 10

This is not possible "Directly", consider this simple program;

 // stove.cpp 

#include <string.h>
#include <stdio.h>
#include <iostream>

using namespace std;

class Foo {
   char* t;
   size_t length;

     public:
     Foo()
     {
       t = new char(8);
       t = static_cast<char*>(static_cast<void*>(this));

     }
      void Print();
};

    void Foo::Print() {
         // what should be ????????? below ?
          cout << this << " " << strlen (t) << t << endl;
    }

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

    you can check the value of t in gdb for both a and b objects,
1) run binary in gdb and put breakpoints on lines where both objects a and b gets created.
2) for both objects a and b , after creation (or in print function, check
print this
print t
    18           }
    (gdb) print this
    $1 = (Foo * const) 0x7fffffffe6e0
    (gdb) print t
    $2 = 0x7fffffffe6e0 "\340\346\377\377\377\177"
    ...
    ...
    ...
    30          Foo b;
    (gdb) s
    Foo::Foo (this=0x7fffffffe6d0) at stov.cpp:15
    15             t = new char(8);
    (gdb) n
    16             t = static_cast<char*>(static_cast<void*>(this));
    (gdb) n
    18           }
    (gdb) print this
    $3 = (Foo * const) 0x7fffffffe6d0
    (gdb) print t
    $4 = 0x7fffffffe6d0 "\320\346\377\377\377\177"

Ответ 11

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

Ответ 12

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

Ответ 13

Вы можете сделать это, используя #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;
}