You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

派生类未实现基类全部纯虚函数的C++多态实现方案咨询

How to Handle Polymorphic Question Types with Different Answer Inputs in C++

The core issue here is your Question base class defines two pure virtual getPoints methods, but each derived class only implements one. Since any concrete derived class must implement all pure virtual methods from its base, QText and QNumeric end up as abstract classes—hence why you can't instantiate them.

There are several valid solutions depending on your needs, from quick fixes to cleaner architectural overhauls:


Solution 1: Implement Unused Methods with Safe Fallback Behavior

A straightforward fix is to provide implementations for the unneeded pure virtual methods in each derived class, but explicitly mark them as invalid to call:

#include <stdexcept>
#include <string>
#include <cmath>

class Question {
public:
    virtual double getPoints(double userAnswer) const = 0;
    virtual double getPoints(const std::string& userAnswer) const = 0;
    virtual ~Question() = default; // Critical for polymorphic memory management!
};

class QText : public Question {
public:
    QText(std::string correct) : correctAnswer(std::move(correct)) {}

    double getPoints(const std::string& userAnswer) const override {
        // Your text validation logic here
        return userAnswer == correctAnswer ? 10.0 : 0.0;
    }

    double getPoints(double userAnswer) const override {
        throw std::invalid_argument("QText does not accept numeric answers");
        // Or return 0.0 if you prefer silent failure (not recommended)
    }

private:
    std::string correctAnswer;
};

class QNumeric : public Question {
public:
    QNumeric(double correct) : correctAnswer(correct) {}

    double getPoints(double userAnswer) const override {
        // Your numeric validation logic here (e.g., tolerance check)
        return std::abs(userAnswer - correctAnswer) < 0.1 ? 10.0 : 0.0;
    }

    double getPoints(const std::string& userAnswer) const override {
        throw std::invalid_argument("QNumeric does not accept text answers");
    }

private:
    double correctAnswer;
};

This works, but shifts responsibility to the caller to pass the correct argument type. Accidentally calling the wrong overload will throw an error (which is better than silent failure).


Solution 2: Redesign with a Variant Answer Type (C++17+)

A cleaner, more type-safe approach uses std::variant to represent the answer, reducing the base class to a single getPoints method:

#include <variant>
#include <string>
#include <stdexcept>
#include <cmath>

// Define a variant type that can hold either a double or string answer
using Answer = std::variant<double, std::string>;

class Question {
public:
    virtual double getPoints(const Answer& userAnswer) const = 0;
    virtual ~Question() = default;
};

class QText : public Question {
public:
    QText(std::string correct) : correctAnswer(std::move(correct)) {}

    double getPoints(const Answer& userAnswer) const override {
        // Check if the variant holds a string
        if (const auto* textAns = std::get_if<std::string>(&userAnswer)) {
            return *textAns == correctAnswer ? 10.0 : 0.0;
        }
        throw std::invalid_argument("QText expects a text answer");
    }

private:
    std::string correctAnswer;
};

class QNumeric : public Question {
public:
    QNumeric(double correct) : correctAnswer(correct) {}

    double getPoints(const Answer& userAnswer) const override {
        // Check if the variant holds a double
        if (const auto* numAns = std::get_if<double>(&userAnswer)) {
            return std::abs(*numAns - correctAnswer) < 0.1 ? 10.0 : 0.0;
        }
        throw std::invalid_argument("QNumeric expects a numeric answer");
    }

private:
    double correctAnswer;
};

Usage becomes explicit and cohesive:

#include <vector>
#include <memory>

int main() {
    std::vector<std::unique_ptr<Question>> quiz;
    quiz.push_back(std::make_unique<QText>("Paris"));
    quiz.push_back(std::make_unique<QNumeric>(42.0));

    // Pass answers as variants
    quiz[0]->getPoints(std::string("Paris"));
    quiz[1]->getPoints(42.0);

    return 0;
}

This approach is preferred for modern C++ code as it centralizes answer type handling and makes invalid states harder to reach.


Solution 3: Visitor Pattern (Advanced)

For complex interactions between question types and answers, the visitor pattern eliminates runtime type checks and exceptions. It requires more boilerplate but is fully type-safe:

#include <variant>
#include <string>
#include <functional>
#include <cmath>

using Answer = std::variant<double, std::string>;

// Visitor interface to handle different question-answer pairs
struct QuestionAnswerVisitor {
    std::function<double(const QText&, const std::string&)> visitText;
    std::function<double(const QNumeric&, double)> visitNumeric;

    double operator()(const QText& q, const std::string& ans) const {
        return visitText(q, ans);
    }

    double operator()(const QNumeric& q, double ans) const {
        return visitNumeric(q, ans);
    }
};

class Question {
public:
    virtual double accept(const QuestionAnswerVisitor& visitor, const Answer& ans) const = 0;
    virtual ~Question() = default;
};

class QText : public Question {
public:
    QText(std::string correct) : correctAnswer(std::move(correct)) {}

    double accept(const QuestionAnswerVisitor& visitor, const Answer& ans) const override {
        if (const auto* textAns = std::get_if<std::string>(&ans)) {
            return visitor(*this, *textAns);
        }
        throw std::invalid_argument("Wrong answer type for QText");
    }

    double calculatePoints(const std::string& userAnswer) const {
        return userAnswer == correctAnswer ? 10.0 : 0.0;
    }

private:
    std::string correctAnswer;
};

// QNumeric implementation follows similar structure...

This is overkill for simple use cases but shines when you have many question types or need to extend behavior without modifying the question classes.


Recommendations

  • Use Solution 1 for a quick, backward-compatible fix.
  • Use Solution 2 for modern, clean C++ code with better type safety.
  • Use Solution 3 only if you need advanced extensibility.

Also, always use smart pointers like std::unique_ptr instead of raw pointers in your vector to avoid memory leaks.

内容的提问来源于stack exchange,提问作者skluzada

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:13:53