Одна из моих функций-членов, которая является чисто виртуальной пустотой, имеет ошибку LNK 2019 [duplicate]

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

$username = $_POST['username'];
$password = $_POST['password'];

$sql = "SELECT * from Users WHERE UserName LIKE '$username'";
$result = mysql_query($sql,$con);

while($row = mysql_fetch_array($result))
{
    echo $row['FirstName'];
}
31
задан PsylentKnight 13 November 2015 в 01:12
поделиться

5 ответов

Существует много статей, объясняющих, почему вы никогда не должны вызывать виртуальные функции в конструкторе и деструкторе на C ++. Посмотрите здесь здесь и здесь , чтобы узнать, что происходит за сценой во время таких вызовов.

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

27
ответ дан Baum mit Augen 26 August 2018 в 11:45
поделиться

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

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

0
ответ дан fatma.ekici 26 August 2018 в 11:45
поделиться

Вы не должны вызывать функцию virtual в конструкторе. Период . Вам нужно найти некоторое обходное решение, например сделать PrintStartMessage не virtual и явно поставить вызов в каждом конструкторе.

5
ответ дан Fred Foo 26 August 2018 в 11:45
поделиться

Ближе всего вы можете сделать что-то подобное, чтобы полностью построить свой объект, а затем вызвать метод после:

template <typename T>
T construct_and_print()
{
  T obj;
  obj.PrintStartMessage();

  return obj;
}

int main()
{
    Derived derived = construct_and_print<Derived>();
}

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

12
ответ дан greatwolf 26 August 2018 в 11:45
поделиться

Вы не можете сделать это так, как вы себе представляете, потому что вы не можете вызывать производные виртуальные функции внутри конструктора базового класса - объект еще не имеет производного типа. Но вам это не нужно.

Вызов PrintStartMessage после построения MyBase

Предположим, что вы хотите сделать что-то вроде этого:

class MyBase {
public:
    virtual void PrintStartMessage() = 0;
    MyBase() {
        printf("Doing MyBase initialization...\n");
        PrintStartMessage(); // ⚠ UB: pure virtual function call ⚠
    }
};

class Derived : public MyBase {
public:
    virtual void PrintStartMessage() { printf("Starting Derived!!!\n"); }
};

Желаемая трассировка выполнения:

Doing MyBase initialization...
Starting Derived!!!

Но для этого нужны конструкторы! Просто отбросьте виртуальную функцию и сделайте конструктор производного для выполнения задания!

class MyBase {
public:
    MyBase() { printf("Doing MyBase initialization...\n"); }
};

class Derived : public MyBase {
public:
    Derived() { printf("Starting Derived!!!\n"); }
};

Результат, ну, что мы ожидаем:

Doing MyBase initialization...
Starting Derived!!!

Это doesn 't принудительно использовать производные классы, чтобы явно реализовать функциональность PrintStartMessage, хотя. Но, с другой стороны, подумайте дважды, действительно ли это необходимо, поскольку они в любом случае всегда могут обеспечить пустую реализацию.

Вызов PrintStartMessage перед конструкцией MyBase

Как было сказано выше, if вы должны называть PrintStartMessage до того, как Derived был конструктором, вы не можете этого сделать, потому что еще нет объекта Derived для вызова PrintStartMessage. Было бы бессмысленно требовать, чтобы PrintStartMessage был нестационарным членом, потому что он не имел бы доступа ни к одному из членов данных Derived.

Статическая функция с заводской функцией

В качестве альтернативы мы можем сделать его статическим членом следующим образом:

class MyBase {
public:
    MyBase() {
        printf("Doing MyBase initialization...\n");
    }
};

class Derived : public MyBase {
public:
    static void PrintStartMessage() { printf("Derived specific message.\n"); }
};

Возникает естественный вопрос о том, как он будет называться?

Есть два решения, которые я вижу: один из них похож на @greatwolf, где вы должны называть его вручную. Но теперь, поскольку это статический член, вы можете вызвать его до того, как был сконструирован экземпляр MyBase:

template<class T>
T print_and_construct() {
    T::PrintStartMessage();
    return T();
}

int main() {
    Derived derived = print_and_construct<Derived>();
}

Выход будет

Derived specific message.
Doing MyBase initialization...

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

Второе решение - прибегнуть к любопытно повторяющейся шаблонной схеме (CRTP). Сообщая MyBase полный тип объекта во время компиляции, он может выполнить вызов внутри конструктора:

template<class T>
class MyBase {
public:
    MyBase() {
        T::PrintStartMessage();
        printf("Doing MyBase initialization...\n");
    }
};

class Derived : public MyBase<Derived> {
public:
    static void PrintStartMessage() { printf("Derived specific message.\n"); }
};

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

Доступ к MyBase из PrintStartMessage с помощью CRTP

Когда выполняется MyBase, его уже ОК, чтобы получить доступ к своим членам. Мы можем сделать PrintStartMessage доступ к вызываемому MyBase, который его назвал:

template<class T>
class MyBase {
public:
    MyBase() {
        T::PrintStartMessage(this);
        printf("Doing MyBase initialization...\n");
    }
};

class Derived : public MyBase<Derived> {
public:
    static void PrintStartMessage(MyBase<Derived> *p) {
        // We can access p here
        printf("Derived specific message.\n");
    }
};

Следующие действия также действительны и очень часто используются, хотя и немного опасны:

template<class T>
class MyBase {
public:
    MyBase() {
        static_cast<T*>(this)->PrintStartMessage();
        printf("Doing MyBase initialization...\n");
    }
};

class Derived : public MyBase<Derived> {
public:
    void PrintStartMessage() {
        // We can access *this member functions here, but only those from MyBase
        // or those of Derived who follow this same restriction. I.e. no
        // Derived data members access as they have not yet been constructed.
        printf("Derived specific message.\n");
    }
};
]

Нет шаблонов solution-redesign

Еще один вариант - немного изменить код. IMO это действительно предпочтительное решение, если вам абсолютно необходимо вызвать переопределенное PrintStartMessage изнутри MyBase.

Это предложение состоит в том, чтобы отделить Derived от MyBase следующим образом:

class ICanPrintStartMessage {
public:
    virtual ~ICanPrintStartMessage() {}
    virtual void PrintStartMessage() = 0;
};

class MyBase {
public:
    MyBase(ICanPrintStartMessage *p) : _p(p) {
        _p->PrintStartMessage();
        printf("Doing MyBase initialization...\n");
    }

    ICanPrintStartMessage *_p;
};

class Derived : public ICanPrintStartMessage {
public:
    virtual void PrintStartMessage() { printf("Starting Derived!!!\n"); }
};

Вы инициализируете MyBase следующим образом:

int main() {
    Derived d;
    MyBase b(&d);
}
7
ответ дан ybungalobill 26 August 2018 в 11:45
поделиться
Другие вопросы по тегам:

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