Может ли конструктор класса С++ узнать его имя экземпляра?
Возможно ли узнать имя или имя экземпляра объекта объекта внутри метода класса? Например:
#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 (¶m == 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;
}