Почему этот виртуальный деструктор вызывает нерешенное внешнее?

Неудовлетворенный другими красивыми принтерами для Ruby, я написал свой собственный ( NeatJSON ), а затем поместил его на JavaScript , включая бесплатный онлайн-форматтер ,

Функции (все необязательные):

  • Установите ширину линии и заверните ее способом, который сохраняет объекты и массивы на одна и та же строка, когда они подходят, обертывая одно значение на линию, когда они этого не делают.
  • Сортировка ключей объекта, если хотите.
  • Выровнять объектные ключи (выровнять двоеточия).
  • Отформатируйте числа с плавающей запятой на определенное число десятичных знаков, не испорчив целые числа.
  • Режим «коротких» оберток помещает открывающие и закрывающие скобки / фигурные скобки в той же строке, что и значения, обеспечивая формат, который некоторые предпочитают.
  • Гранулированное управление интервалом для массивов и объектов между скобками, до / после двоеточий и запятых.
  • Функция предоставляется как для веб-браузеров, так и для Node.js .

Я скопирую исходный код здесь, чтобы это была не просто ссылка на библиотеку, но я рекомендую вам перейти на страницу проекта g3HitHub , так как это будет обновляться, а код ниже не будет.

(function(exports){
exports.neatJSON = neatJSON;

function neatJSON(value,opts){
  opts = opts || {}
  if (!('wrap'          in opts)) opts.wrap = 80;
  if (opts.wrap==true) opts.wrap = -1;
  if (!('indent'        in opts)) opts.indent = '  ';
  if (!('arrayPadding'  in opts)) opts.arrayPadding  = ('padding' in opts) ? opts.padding : 0;
  if (!('objectPadding' in opts)) opts.objectPadding = ('padding' in opts) ? opts.padding : 0;
  if (!('afterComma'    in opts)) opts.afterComma    = ('aroundComma' in opts) ? opts.aroundComma : 0;
  if (!('beforeComma'   in opts)) opts.beforeComma   = ('aroundComma' in opts) ? opts.aroundComma : 0;
  if (!('afterColon'    in opts)) opts.afterColon    = ('aroundColon' in opts) ? opts.aroundColon : 0;
  if (!('beforeColon'   in opts)) opts.beforeColon   = ('aroundColon' in opts) ? opts.aroundColon : 0;

  var apad  = repeat(' ',opts.arrayPadding),
      opad  = repeat(' ',opts.objectPadding),
      comma = repeat(' ',opts.beforeComma)+','+repeat(' ',opts.afterComma),
      colon = repeat(' ',opts.beforeColon)+':'+repeat(' ',opts.afterColon);

  return build(value,'');

  function build(o,indent){
    if (o===null || o===undefined) return indent+'null';
    else{
      switch(o.constructor){
        case Number:
          var isFloat = (o === +o && o !== (o|0));
          return indent + ((isFloat && ('decimals' in opts)) ? o.toFixed(opts.decimals) : (o+''));

        case Array:
          var pieces  = o.map(function(v){ return build(v,'') });
          var oneLine = indent+'['+apad+pieces.join(comma)+apad+']';
          if (opts.wrap===false || oneLine.length<=opts.wrap) return oneLine;
          if (opts.short){
            var indent2 = indent+' '+apad;
            pieces = o.map(function(v){ return build(v,indent2) });
            pieces[0] = pieces[0].replace(indent2,indent+'['+apad);
            pieces[pieces.length-1] = pieces[pieces.length-1]+apad+']';
            return pieces.join(',\n');
          }else{
            var indent2 = indent+opts.indent;
            return indent+'[\n'+o.map(function(v){ return build(v,indent2) }).join(',\n')+'\n'+indent+']';
          }

        case Object:
          var keyvals=[],i=0;
          for (var k in o) keyvals[i++] = [JSON.stringify(k), build(o[k],'')];
          if (opts.sorted) keyvals = keyvals.sort(function(kv1,kv2){ kv1=kv1[0]; kv2=kv2[0]; return kv1kv2?1:0 });
          keyvals = keyvals.map(function(kv){ return kv.join(colon) }).join(comma);
          var oneLine = indent+"{"+opad+keyvals+opad+"}";
          if (opts.wrap===false || oneLine.lengthkv2?1:0 });
            keyvals[0][0] = keyvals[0][0].replace(indent+' ',indent+'{');
            if (opts.aligned){
              var longest = 0;
              for (var i=keyvals.length;i--;) if (keyvals[i][0].length>longest) longest = keyvals[i][0].length;
              var padding = repeat(' ',longest);
              for (var i=keyvals.length;i--;) keyvals[i][0] = padRight(padding,keyvals[i][0]);
            }
            for (var i=keyvals.length;i--;){
              var k=keyvals[i][0], v=keyvals[i][1];
              var indent2 = repeat(' ',(k+colon).length);
              var oneLine = k+colon+build(v,'');
              keyvals[i] = (opts.wrap===false || oneLine.length<=opts.wrap || !v || typeof v!="object") ? oneLine : (k+colon+build(v,indent2).replace(/^\s+/,''));
            }
            return keyvals.join(',\n') + opad + '}';
          }else{
            var keyvals=[],i=0;
            for (var k in o) keyvals[i++] = [indent+opts.indent+JSON.stringify(k),o[k]];
            if (opts.sorted) keyvals = keyvals.sort(function(kv1,kv2){ kv1=kv1[0]; kv2=kv2[0]; return kv1kv2?1:0 });
            if (opts.aligned){
              var longest = 0;
              for (var i=keyvals.length;i--;) if (keyvals[i][0].length>longest) longest = keyvals[i][0].length;
              var padding = repeat(' ',longest);
              for (var i=keyvals.length;i--;) keyvals[i][0] = padRight(padding,keyvals[i][0]);
            }
            var indent2 = indent+opts.indent;
            for (var i=keyvals.length;i--;){
              var k=keyvals[i][0], v=keyvals[i][1];
              var oneLine = k+colon+build(v,'');
              keyvals[i] = (opts.wrap===false || oneLine.length<=opts.wrap || !v || typeof v!="object") ? oneLine : (k+colon+build(v,indent2).replace(/^\s+/,''));
            }
            return indent+'{\n'+keyvals.join(',\n')+'\n'+indent+'}'
          }

        default:
          return indent+JSON.stringify(o);
      }
    }
  }

  function repeat(str,times){ // http://stackoverflow.com/a/17800645/405017
    var result = '';
    while(true){
      if (times & 1) result += str;
      times >>= 1;
      if (times) str += str;
      else break;
    }
    return result;
  }
  function padRight(pad, str){
    return (str + pad).substring(0, pad.length);
  }
}
neatJSON.version = "0.5";

})(typeof exports === 'undefined' ? this : exports);

15
задан Rakib 4 August 2014 в 04:31
поделиться

7 ответов

Ситуация 1:

У вас есть код для конструктора.
Таким образом, конструктор встраивается в объектный файл. Конструктору нужен адрес деструктора, чтобы поместить его в виртуальную таблицу, потому что он не может найти его, конструктор не может быть построен.

Ситуация 2: (встроенный конструктор)

Компилятор решает, что ему не нужно создавать конструктор (так как он будет встроен).
Таким образом, он не устанавливает никакого кода и, следовательно, не нуждается в адресе деструктора.

Если вы создаете экземпляр объекта типа X, он снова будет жаловаться.

Ситуация 3: (не виртуальный деструктор)

Для построения конструктора адрес деструктора не нужен.
Так что не жалуется.

Будет выдаваться ошибка, если вы создадите экземпляр объекта типа X.

22
ответ дан 1 December 2019 в 01:16
поделиться

Вам нужно передать тело виртуальному деструктору:


class X
{
    X();
    virtual ~X() {}
};
6
ответ дан 1 December 2019 в 01:16
поделиться

В C ++ функции должны быть определены тогда и только тогда, когда они используются в вашей программе (см. ODR в 3.2 / 2). Как правило, невиртуальные функции используются , если они вызываются из потенциально вычисляемых выражений. Любая нечистая виртуальная функция считается безусловно использованной . Когда используются [невиртуальные] специальные функции-члены , определяется в выделенных местах стандарта языка. И так далее.

  • В вашем первом примере вы объявили свой деструктор как нечистую виртуальную функцию. Это сразу означает, что ваш деструктор используется в вашей программе. Это, в свою очередь, означает, что требуется определение этого деструктора. Вы не смогли предоставить определение, поэтому компилятор сообщил об ошибке.

  • В вашем третьем примере деструктор не виртуальный. Поскольку вы не используете деструктор в своей программе, определение не требуется, и код компилируется (см. 12.4 для подробного описания того, что составляет использование деструктора).

  • В вашем втором примере вы имеете дело с причудой реализации, вызванной тем фактом, что конструктор встроен. Поскольку деструктор не является чисто виртуальным, определение требуется. Однако вашему компилятору не удалось обнаружить ошибку, поэтому код, похоже, компилируется успешно. Вы можете найти причины такого поведения в деталях реализации, но с точки зрения C ++ этот пример так же сломан, как и первый, по той же причине.

5
ответ дан 1 December 2019 в 01:16
поделиться

Возможно, вам это сойдет с рук, потому что и constr, и destr являются закрытыми - если в вашей сборке нет другой ссылки на класс X, то компилятор может сделать вывод, что destr не требуется, так что отсутствие определения не имеет большого значения.

Это не объясняет мне, почему вариант 1 терпит неудачу, а 2 и 3 - нормально. Интересно, что произойдет, если оба станут достоянием общественности?

0
ответ дан 1 December 2019 в 01:16
поделиться

Ответ на ваш первый вопрос,

Что вызывает неразрешенный внешний компоновщик, когда .dtor виртуальный или когда .ctor не встроен?

... просто у вас нет определения деструктора.

Теперь ваш второй вопрос несколько интереснее:

почему я не получаю нерешенный внешний, если я сделаю деструктор невиртуальный, или если я встрою конструктор?

И причина в том, что ваш компилятор не нуждался в деструкторе X, поскольку вы никогда не создавали экземпляр X, поэтому он выбросил весь ваш класс.Если вы попытаетесь скомпилировать эту программу, вы получите неразрешенный внешний код:

class X
{
public:
    X();
     ~X();
};

X::X() {};

int main()
{
    X x;
    return 0;
}

Но если вы закомментируете X x;, она, как вы заметили, скомпилируется просто отлично.

Теперь давайте вернемся к тому, почему он не будет компилироваться, если деструктор if virtual. Я размышляю здесь, но я полагаю, что причина в том, что, поскольку у вас есть виртуальный деструктор, X теперь является полиморфным классом. Чтобы размещать полиморфные классы в памяти, компиляторам, реализующим полиморфизм с помощью vtable, нужны адреса каждой виртуальной функции. Вы не реализовали X::~X, поэтому неразрешенный внешний результат.

Почему компилятор просто не отбрасывает X, как это было, когда X не был полиморфным классом? Здесь больше предположений. Но я предполагаю, что причина в том, что даже если вы не создавали экземпляр X напрямую, нельзя быть уверенным, что нигде в вашем коде X не живет, маскируясь под что-то другое. В качестве примера рассмотрим абстрактный базовый класс. В этом случае вы никогда не будете создавать экземпляр Base напрямую, а код для Derived может находиться в совершенно отдельной единице перевода. Поэтому, когда компилятор доберется до этого полиморфного класса, он не сможет его отбросить, даже если не знает, что вы создали его экземпляр.

2
ответ дан 1 December 2019 в 01:16
поделиться

У меня есть подозрение, что это поведение определяется реализацией. Вот почему

$10.3/8- "Виртуальная функция, объявленная в классе должны быть определены, или объявлен чистым (10.4) в этом классе, или оба; но диагностика не требуется (3.2)."

GCC выдает ошибку, как показано ниже, что опять же очень наводит на размышления (по крайней мере, для меня) о нестандартных деталях реализации виртуальных функций

/home/OyXDcE/ccS7g3Vl.o: В функции X::X()': prog.cpp:(.text+0x6): неопределенная ссылка на vtable для X' /home/OyXDcE/ccS7g3Vl.o: в работе X::X()': prog.cpp:(.text+0x16): неопределенная ссылка на vtable для X' collect2: ld вернул 1 статус выхода

Я запутался, действительно ли требуется диагностика от компилятора для кода OP, поэтому подумал опубликовать это, даже несмотря на то, что я рискую получить отрицательные голоса :). Конечно, хороший компилятор должен я предполагать.

1
ответ дан 1 December 2019 в 01:16
поделиться

Это еще не полная программа (и даже не полная DLL). Когда вы получаете сообщение об ошибке, на самом деле вам помогают, потому что X непригоден для использования без определения ~X()

Все это означает, что этому конкретному экземпляру компилятора в некоторых случаях требовалось определение для него. Даже если он компилируется, он ничего не делает.

1
ответ дан 1 December 2019 в 01:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: