每个子类中的静态常量变量

Static const variable in each subclass

我目前正在为 SQL 数据库 table 构建一个接口。我当前的 class 层次结构如下所示,列出了一些基本方法和变量:

class AbstractTable
{
public:
    AbstractTable(QString tableName);
    void addRow(); // it operates on tableName and colNames
private:
    const QString tableName;
    const QStringList colNames;
    //... some more, for example:
    const QString identificationCollumn;
}

class ProductsTable : public AbstractTable
{
    ProductsTable();
}

class UsersTable: public AbstractTable
{
    UsersTable();
}

重点是,AbstractTable::tableNameAbstractTable::colNames 在对象创建期间初始化,并在特定的 xxxTable 构造函数中设置。还有一些其他的const变量。

现在,我的表在整个程序中有很多实例(我无法减少它,因为某些功能是基于上下文的),我想减少代码中存在的冗余。我想到的第一个想法是让 AbstractTable::tableNameAbstractTable::colNames 都是静态的,但它只会为所有 class 引入一个字段(我需要为每个子class类型)。我将不得不从 AbstractTable 中删除 tableNamecolNames 初始化并将其移动到从 AbstractTable 继承的所有 classes - 然后我就可以标记它 static。它并没有真正解决问题,因为方法 addRow 应该能够在 tableNamecolNames 上运行。它可以通过创建一个虚拟方法 virtual QString AbstractTable::getTableName() 来解决,该方法将在每个子 class 中实现(即 ProductsTable::getTableName() 将 return 一个值为“产品”的静态字段)。但下一点说明了为什么它不是解决方案:

最后,我想为每个 table 设置静态方法 addRow(),因此调用 ProductsTable::addRow() 会向 table 添加一行命名为 Products,似乎有必要在每个子 class 中实现,这将对每个子 class.

产生大量复制粘贴

看来我必须将所有字段移至子class,让父class毫无意义。 tableNamecolNames 必须在子 class 中实现,即使这些变量在每个子 class 中都是相同的,addRow() 需要移动到 subclass,因为调用 ProductsTable::addRow() 需要向 Products 添加行,而 UsersTable::addRow() 需要向 users 添加行。

tableNamecolNames的重点是让抽象class发挥作用。所以我可以实现一个指令q.exec("SELECT ("+colNames.join(',')+") FROM "+tableName);,一个解决方案可以在每个子class中实现addRow逻辑,使我的整个结构毫无意义,因为ProductsTable::select(),可以调用q.exec("SELECT (a,b,c,d,e) FROM Products"); - 因为 colNames 和 tableName 是常量。

有没有办法在 C++ 中解决这个问题,同时保留父 class 实现,因为它对所有子 class 都是通用的?

一个简单的方法是将特定于实例的行为和所有实例共享的行为拆分为两个类,即引入一个额外的间接。

struct TableDescriptor {
    std::string name;
    std::vector<std::string> cols;
};

class AbstractTable {
protected:
    AbstractTable(const TableDescriptor& descriptor)
        : descriptor(descriptor) 
    {
        // Feel free to change to your prefered way of storing refs if you feel uncomfortable with storing const refs
        // e.g shared_ptr or theirlike
        // if you want to modify the descriptor here, just pass a regular reference an not a const ref
    }

private:
    const TableDescriptor& descriptor;  // 8 bytes per instance on x64
};

class UserTable : public AbstractTable {
    static TableDescriptor user_desc;

public:
    UserTable()
        : AbstractTable(user_desc)
    {
    }
};

TableDescriptor UserTable::user_desc{"user_table", {"id", "name"}};

如果每个 table 都有一些公共行,它们可以自动添加到 TableDescriptor ctor 中以防止不必要的复制粘贴。