Объявление переменной внутри блока if в c

#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]?