非虚拟接口中具有链式方法的 getter

A getter in a non-virtual interface with a chained method

提问人:Namal 提问时间:4/25/2023 最后编辑:Namal 更新时间:4/25/2023 访问量:60

问:

我有以下接口,可以返回一个映射,其中包含一对 int 和 string 作为键和字符串作为值。

class Foo {
    public:
        virtual ~Foo() = default;
        Foo& operator=( const Foo& ) = delete; 
        std::map<std::pair<unsigned int, std::string>, std::string> save_to_map(const std::string filename) {return getRecord(filename);}

    private:
        virtual std::map<std::pair<unsigned int, std::string>, std::string> getRecord(const std::string filename) = 0;
};

class Bar final : public Foo {
    public:
        Bar() = default;

    private:    
        std::map<std::pair<unsigned int, std::string>, std::string> getRecord(const std::string filename) override {
            std::map<std::pair<unsigned int, std::string>, std::string> fullRecord; 
            fullRecord[std::make_pair(0, "A")] = "text";
            fullRecord[std::make_pair(0, "B")] = "2";
            fullRecord[std::make_pair(0, "C")] = "3.41";
            
            return fullRecord;
        }
};

我正在使用工厂函数初始化对象,并且可以以通常的方式访问映射的值。

std::unique_ptr<Foo > fac_func(){
    auto dbi = std::make_unique<Bar>();
    
    return dbi;
} 

int main(){
    auto p = fac_func();
    auto m = p ->save_to_map("path\to\file");
    
    std::cout<<m[std::make_pair(0, "A")]<<'\n';
    std::cout<<m.at(std::make_pair(0, "B"))<<'\n';
}

现在,我想通过键的两个值访问值,并添加一个链接方法,如果可能的话,将值转换为标准类型的 int、double 或字符串。但在我的脑海中,我没有掌握实现这一目标的最佳实践。比如,作为私人会员,我是否需要额外的地图,或者我是否需要一种特殊的工厂功能?最后,它应该是这样的:

std::string s = p -> Get(0, "A").AsString();
int i = p -> Get(0, "B").AsInt();

编辑:根据要求,我正在添加我删除的其他代码,以便有一个最小的示例。所以 getRecord 看起来像这样:

        std::map<std::pair<unsigned int, std::string>, std::string> getRecord(const std::string tableName) override {
            std::map<std::pair<unsigned int, std::string>, std::string> fullRecord;
            column_names = get_column_names(tableName);
            std::string sqlExpr = "Select * from " + tableName;
            tableSelect(fullRecord, sqlExpr, column_names);

            return fullRecord;
        }

这是 tableSelect

void SqliteDB :: tableSelect(std::map<std::pair<unsigned int, std::string>, std::string>  &record_map, const std::string sqlExpr, const std::vector<std::string> &params){

    unsigned int columnCount;
    const unsigned char *columnText;
    sqlite3_stmt *stmt;

    int rc = sqlite3_prepare_v2(sqlDb, sqlExpr.c_str(), -1, &stmt, NULL);

    if(!params.empty()){
        for(size_t i = 0; i<params.size(); ++i){
            sqlite3_bind_text(stmt, i+1, params[i].c_str(), params[i].length(), SQLITE_TRANSIENT);
        }
    }

    if (rc != SQLITE_OK) {
        std::cout<<"error: "<<sqlite3_errmsg(sqlDb)<<'\n';
     return;
    }

    for(size_t j = 0; sqlite3_step(stmt) == SQLITE_ROW; ++j) {

        columnCount = sqlite3_data_count(stmt);
        for(size_t i = 0; i<columnCount; ++i){
            columnText = sqlite3_column_text(stmt, i);
            if(columnText != NULL){
                record_map[std::make_pair(j, params[i])] = reinterpret_cast<const char*>(columnText);
            }
        }
    }
    sqlite3_finalize(stmt);
}
C++ 方法 getter-setter

评论

0赞 Ted Lyngmo 4/25/2023
这看起来非常低效。你为什么要按值传递完整的 s?在你拿到地图之后,对那张地图一无所知。将映射保留在内部,并改为添加查询成员函数。另外,什么是?它没有被使用mapfac_funcp->GetFooBartableName
0赞 Ted Lyngmo 4/25/2023
如果你更多地解释一下它应该用于什么以及“文件名”在哪里发挥作用的细节,那么提供答案会更容易。
0赞 Namal 4/25/2023
@TedLyngmo文件名只是一个 sqlite 文件的路径,其中包含一个数据库,我从中读取表,我正在读取记录并按行索引和列名将它们存储到映射中。桌子很小,所以我认为我使用地图并不重要。
0赞 Ted Lyngmo 4/25/2023
好的,但是你能展示一下你是如何在代码中使用它的吗?现在有一个断开连接,因为根本没有使用它。另外,如果你读取数据库,你没有得到表中的正确类型吗?getRecord
0赞 Namal 4/25/2023
@TedLyngmo SQLite 有一个功能,它不在乎声明的列类型。例如,没有什么可以阻止您在整数列中添加文本。这就是为什么我决定将所有内容都读取为字符串,并在可能的情况下使用链接方法进行转换。我感觉地图的方法在这里可能不是必需的,但我按照您的要求添加了它。

答:

0赞 fana 4/25/2023 #1

准备一个具有方法、等的类。AsStringAsInt

class StrUtil
{
private:
    const std::string &m_rStr;
public:
    StrUtil( const std::string &Str ) : m_rStr{Str} {}
    const std::string &AsString() const {   return m_rStr;  }
    int AsInt() const { return std::stoi( m_rStr ); }
};

接下来,创建类似 map 的类,该类提供方法返回而不是原始 。StrUtilstd::string

class MapLike
{
public:
    MapLike()
    {//This is test code
        m_Map[ std::make_pair( 0, "A") ] = "text";
        m_Map[ std::make_pair( 0, "B") ] = "2";
        m_Map[ std::make_pair( 0, "C") ] = "3.14";
    }
public:
    StrUtil operator()( unsigned int Key1, const std::string &Key2 ) const
    {
        auto iElem = m_Map.find( std::make_pair(Key1,Key2) );
        return StrUtil( iElem!=m_Map.end()  ?  iElem->second  :  "" );
    }
private:
    std::map< std::pair<unsigned int, std::string>, std::string > m_Map;
};

//test
int main()
{
    MapLike M;
    std::cout << M( 0, "A" ).AsString() << '\n';
    std::cout << M( 0, "B" ).AsInt() << '\n';
    return 0;
}

评论

0赞 Namal 4/25/2023
这似乎不适用于 AsInt,因为它崩溃了。
0赞 fana 4/26/2023
是的,首先,“解析为整数”之类的过程不能适用于所有任意字符串。这个示例代码不处理这样的点,因为我认为这样的点不是这个问题的重点