#c #scope
#c #область видимости
Вопрос:
Я новичок в c и часто ловлю себя на том, что хочу объявить тип переменной в зависимости от некоторого внешнего ввода, такого как ввод из командной строки или чтение из текстового файла.
Например
int main(int argc, char *argv[]) {
if (argv[1] == "thing1") {
Thing1 thing;
} else if (argv[1] == "thing2") {
Thing2 thing;
}
// More code
}
Это не работает, потому что я не могу использовать переменную thing вне блока if, поэтому мой вопрос в том, как добиться этой функциональности?
Комментарии:
1. Можете ли вы привести конкретный пример?
2. Совместимы ли Thing1 и Thing2?
Ответ №1:
Похоже, вы хотите использовать некоторый полиморфизм во время выполнения. В C вы не можете выполнять полиморфизм с объектами стека — самое близкое, что вы можете сделать, это подделать его, объявив оба объекта в стеке во внешней области видимости, а затем используя только один из них, вот так:
int main(int argc, char *argv[]) {
Thing1 thing1;
Thing2 thing2;
bool useThing1 = true;
if (strcmp(argv[1], "thing1") == 0) {
useThing1 = true;
} else if (strcmp(argv[1], "thing2") == 0) {
useThing1 = false;
}
if (useThing1)
{
thing1.DoSomething();
}
else
{
thing2.DoSomething();
}
[... rest of code ...]
}
… но это не очень удовлетворительно и не будет хорошо масштабироваться, если вам нужно более двух типов Thing
.
Лучшим подходом является использование наследования и динамического распределения, например:
class ThingBase
{
public:
ThingBase() {}
virtual ~ThingBase() {}
virtual void DoSomething() = 0;
};
class Thing1 : public ThingBase
{
public:
Thing1() {}
virtual void DoSomething() {std::cout << "Thing1::DoSomething() called" << std::endl;}
};
class Thing2 : public ThingBase
{
public:
Thing2() {}
virtual void DoSomething() {std::cout << "Thing2::DoSomething() called" << std::endl;}
};
int main(int argc, char *argv[]) {
ThingBase * thing = NULL;
if (strcmp(argv[1], "thing1") == 0) {
thing = new Thing1;
} else if (strcmp(argv[1], "thing2") == 0) {
thing = new Thing2;
}
if (thing)
{
thing->DoSomething();
[... rest of code ...]
delete thing;
}
}
… этот способ лучше, потому [... rest of code..]
что раздел of main()
не должен знать (или заботиться) о том, с каким подклассом ThingBase
он работает; в частности, он может просто вызвать DoSomething()
метод по своему thing
указателю, и соответствующая реализация метода будет вызвана автоматически. Это помогает упростить вызывающий код (что становится все более важным, когда вы начинаете добавлять больше типов Thing
)
Комментарии:
1. Разве это не должно использоваться
std::unique_ptr
вместо naked new?2. Конечно, это был бы очень хороший способ избежать любой возможности утечки памяти, но для того, чтобы не усложнять пример кода дополнительными понятиями, я пока оставлю его как есть.
3. Вы забыли о std::variant
4. Я согласен с мнением @JeremyFriesner — слишком часто ответ на один вопрос вводит 5 дополнительных понятий, о которых спрашивающий еще не узнал.
Ответ №2:
Поскольку Thing1
и Thing2
являются разными классами, вам понадобятся thing
переменные с разным типом. Лучший вариант — переместить «больше кода» в функции и вызывать их для Thing1
или Thing2
из вашего блока.
Ответ №3:
как java отражает:
#include <stdio.h>
#include <stdlib.h>
class Base {
public:
Base() {
printf("CalssBasen");
}
};
class A : public Base {
public:
A() {
printf("CalssAn");
}
};
class REG_A {
public:
static Base* newInst() {
return new A();
}
};
Base* p;
#define CREATE(name)
p = REG_##name::newInst();
int main(void) {
CREATE(A);
return 0;
}
Комментарии:
1. И как вы заставляете ее меняться в зависимости от argv[1]?