Наследование и защита статических членов

У меня есть вопрос о наследовании статических членов и их защите в C++. Я надеюсь, что я буду достаточно ясен, так как не всегда легко записывать состояния ума :) Я пишу простой (текстовый синтаксический анализатор) для графической программы, которая загружает текстовый файл с пользовательским форматированием, теперь текстовая часть почти готова и теперь мне нужно создать несколько объектов, чтобы передать им данные, которые я загрузил из файла.

Я считаю, что этот вопрос относится к часу 1 C++, но я застрял. Например, я загрузил из текстового файла только 2 типа логических «узлов», LAYER и PLINE, у них также есть атрибуты, которые могут быть или не быть общими для обоих. Отношение LAYER к PLINE и обратно сейчас совершенно не имеет значения, меня беспокоит то, как подключить и обрабатывать атрибуты обоих:

Предположим, я выбрал DataObj в качестве базового класса для обоих. DataObj имеет элемент с именем «имя», потому что и LAYER, и PLINE могут иметь имя. СЛОЙ имеет атрибут, который является общим только для слоя, например. «заблокировано», а PLINE имеет атрибут, который является общим только для pline, например. "цвет". В «школьном стиле» это выглядело бы так:

/// i use const char* for everything to not complicate things ... 
...
class DataObj {
  ...
  const char* name;
  ...
}
...
class Layer : public DataObj {
 ...
 const char* locked;
 ...
}
...
class Pline : public DataObj {
 ...
 const char* color;
 ... 
}
...
int main(){
   Layer* l = new Layer();
   l.name = "first layer";
   l.locked = "false";

   Pline* p = new Pline();
   p.name = "wonderful line";
   p.color = "0xFF3300";
}
...

Теперь я хочу сделать это более «динамически», так как я действительно не беспокоюсь о статических типизированных именах членов (и, возможно, их аксессорах в будущем), особенно при подаче объектов данными, поступающими из синтаксического анализатора. Я имею в виду, что это легко сделать только с двумя типами узлов, но у меня их будет более дюжины.

Итак, концепция, которую я хочу сделать, состоит в том, чтобы «статически» нажимать вектор разрешенных атрибутов для каждого типа узла (класса), а затем только выполнять проверки, разрешен ли этот атрибут в объекте, и устанавливать его во время синтаксического анализа. Возможно, я хочу иметь 2 важных члена: 1. это std::map пар kv, второй - статический вектор разрешенных атрибутов для определенного узла. Следуя коду, введенному ранее:

...
class DataObj {
  ...
  static std::vector<const char*> allowedAttrs;
  std::map <const char*, const char*> attrs;

  private: 
     static bool isInit;
  ...
}
...
DataObj::DataObj(){
  if(!isInit)
    allowedAttrs.push_back("name");
  isInit = true;
}
...
Layer::Layer(){
  if(!isInit) // private static for Layer
     allowedAttrs.push_back("locked");
}
...
Pline::Pline(){
  if(!isInit) // private static for Pline
     allowedAttrs.push_back("color");
}
...

Проблема, которую я получаю здесь, вероятно, видна с Луны. Если мы инициируем сначала новый слой, а затем новый Pline, Pline будет иметь имя, заблокировано и цвет в векторе allowAttrs, и это неправильно, потому что «заблокировано» должно быть действительным только для узла слоя.

Поэтому мне нужен какой-то способ решить эту проблему таким образом, чтобы член «allowedAttrs» стал «частным» для необычных атрибутов, таких как «заблокировано» в объекте слоя, но также сохранил свой «общедоступный» характер из суперкласса «DataObj» - поэтому он может перехватывать общие атрибуты, такие как «имя». Другими словами, я не хочу «разбивать» «поток наследования», идущий вверх к базовому классу, и определять новую переменную для каждого класса узла (объекта), повторяя один и тот же код снова и снова. (что-то вроде виртуальной переменной).

Я надеюсь, что этот вопрос не (такой) глупый, и я действительно буду признателен за ваши ответы.


c++
person cybercow    schedule 01.10.2012    source источник
comment
Пожалуйста, сформулируйте свой вопрос кратко и по существу. Это поможет вам быстро получить ответы.   -  person Sidharth Mudgal    schedule 02.10.2012
comment
поскольку сначала вызывается конструктор базового класса, будут ли когда-либо происходить другие push_backs?   -  person moooeeeep    schedule 02.10.2012
comment
Я думаю, вы совершенно неправильно поняли, как работает static   -  person Mooing Duck    schedule 02.10.2012
comment
i use const char* for everything to not complicate things Неверно, ИМО. Вместо этого вы должны были использовать std::string.   -  person Mahesh    schedule 02.10.2012
comment
что не так с const char* ?   -  person cybercow    schedule 02.10.2012
comment
в этом нет ничего плохого, это только усложняет ситуацию. строковый объект намного проще в работе.   -  person user1708860    schedule 02.10.2012


Ответы (2)


1) Я бы использовал отдельные статические члены для каждого типа, чтобы хранить разрешенные атрибуты для каждого типа.
2) Переместите статические члены в функции, что безопаснее и, возможно, позволит избежать проверки того, инициализируется ли он все время (в зависимости от насколько хорош ваш компилятор и другие подробности)
3) Не используйте const char*, за исключением очень специфических случаев. Если вы не знаете, что это за вещи, всегда используйте std::string. В этом случае мы должны использовать std::string.
4) Я изменил allowedAttrs с вектора на set, что может быть быстрее для большого количества атрибутов и, возможно, медленнее для меньшего числа.

Вот база:

class DataObj {
  const std::set<std::string>& get_allowed_data_attributes() static {    
    static std::set<std::string> allowedAttrs = {"name"};
    return allowedAttrs;
  }
  std::map <std::string, std::string> attrs;
  public:
     DataObj(){ }
     void set_attribute(std::string key, std::string value) {
         auto it = get_allowed_data_attributes().find(key);
         if (it  == get_allowed_data_attributes().end())
            throw bad_key_exception(key);
         attrs.insert(std::make_pair(std::move(key), std::move(value)));
     }
     const std::string& get_attribute(const std::string& key) const {
         auto it = attrs().find(key);
         if (it  == attrs().end())
            throw bad_key_exception(key);
         return it->second;
     }
};

Вот производное:

class Layer : public DataObj {
  const std::set<std::string>& get_allowed_data_attributes() static {    
    static std::set<std::string> allowedAttrs = {"locked"};
    return allowedAttrs;
  }
  public:
     DataObj(){ }
     void set_attribute(std::string key, std::string value) {
         auto it = get_allowed_data_attributes().find(key);
         if (it  == get_allowed_data_attributes().end())
            DataObj::set_attribute(std::move(key), std::move(value));
         else
            attrs.insert(std::make_pair(std::move(key), std::move(value)));
     }
     const std::string& get_attribute(const std::string& key) const {
         auto it = attrs().find(key);
         if (it  == attrs().end())
            return DataObj::get_attribute(key);
         else
            return it->second;
     }
};

Обратите внимание, что если вы дадите ему неверный ключ, он выдаст bad_key_exception, который вам придется добавить. Убедитесь, что он наследуется от std::runtime_error.

person Mooing Duck    schedule 01.10.2012
comment
Спасибо также за мощное решение и советы. У меня один вопрос, можно ли методы set_attribute и get_attribute определить только один раз в базовом классе как protected? - person cybercow; 02.10.2012
comment
@cybercow: конечно, это прекрасно работает. (Кроме того, я исправил ошибку в коде) - person Mooing Duck; 02.10.2012
comment
Еще раз спасибо, это действительно даст толчок моему проекту! У меня действительно не было возможности использовать std::string(s), и, возможно, мое первое предположение заключалось в том, что использование c-строк приведет к более быстрому коду? Парсер, который я пишу, будет анализировать ~ полмиллиона строк необработанных текстовых данных (в основном пространственные координаты) и передавать стек объектов, как в примере, который вы закодировали. Так, например, у вас может быть 100 слоев и 100000 линий. Предположительно, с моей стороны было глупо предполагать, что c-строки быстрее, но я начал с c-струн и не хотел смешивать их вместе. - person cybercow; 02.10.2012
comment
@cybercow: cstrings работают быстрее, но только если вы правильно их понимаете, а увеличение скорости похоже на потерю 20 фунтов перед тем, как сесть за руль автомобиля. А если ошибешься, машина взорвется. Используйте 1_. Всегда делайте это правильно, а о скорости беспокойтесь позже. - person Mooing Duck; 02.10.2012

Вот как я бы реализовал ваш вектор разрешенных атрибутов. Я бы хотел, чтобы каждый тип содержал список разрешенных атрибутов, копируя список своего базового класса во время инициализации.

class DataObj {
  ...
  static std::vector<std::string> allowedAttrs;
  std::map <std::string, std::string> attrs;

  private: 
     static bool isInit;
  ...
}
class Layer {
  static std::vector<std::string> allowedAttrs;
  ...
};
class PLine {
  static std::vector<std::string> allowedAttrs;
}
...
DataObj::DataObj(){
  if(!isInit)
    allowedAttrs.push_back("name");
  isInit = true;
}
...
Layer::Layer(){
  if(!isInit) { // private static for Layer
     allowedAttrs = DataObj::allowedAttrs;
     allowedAttrs.push_back("locked");
  }
}
...
Pline::Pline(){
  if(!isInit) { // private static for Pline
     allowedAttrs = DataObj::allowedAttrs;
     allowedAttrs.push_back("color");
  }

}

Заметки:

  • Используйте std::string, а не char* как для вектора, так и для карты. Хотя vector<char*> потенциально может быть полезным, map<char*,char*> — это просто ошибка.

  • Вы можете избежать дублирования списка базового класса, сделав функцию поиска немного умнее.

person Robᵩ    schedule 01.10.2012
comment
Спасибо за ваш ответ, я очень ценю его, ну, я только что наконец-то покопался в C ++, и в основном старые документы всегда каким-то образом используют char в некоторых его вариациях, поэтому мои мысли были правильными для его использования. - person cybercow; 02.10.2012
comment
Не каждое использование char* или const char* является неправильным, но их достаточно. Я никогда не рекомендую char* начинающим программистам на C++. - person Robᵩ; 02.10.2012