2

我在使用 Boost 共享指针时遇到了一些问题。指针指向的数据在不确定的时间突然被删除。在当前程序中(我尝试了许多其他变体),在 case case 之后,在循环恰好运行了 172 行之后(表中有 1000 行),数据boost::shared_array<TableFieldsMap> retArray在 switch 构造中突然被删除。MYSQL_TYPE_DOUBLE但仅当该方法被调用两次时。第一次运行完美。

具体来说,我将问题定位为在分配给 retArray 时使用的 map-key。当我使用硬编码字符串时它可以工作。但不是本地 shared_array (fieldNameArray) 的值(我首先有 scoped_array,但我理解这不应该工作),而不是本地变量 (tmpIndex)。

retArray[rowIndex]["value"] = boost::lexical_cast<long double>(row[i]); // This works.
// This doesn't work: retArray[rowIndex][fieldNameArray[i]] = boost::lexical_cast<long double>(row[i]);
// This doesn't work either: retArray[rowIndex][tmpIndex.c_str()] = boost::lexical_cast<long double (row[i]);

这是标题类:

#ifndef DATABASE_H
#define DATABASE_H

#include <map>
#include <string>
#include <iostream>
#include <vector>
using namespace std;

#include <glog/logging.h>
#include <boost/lexical_cast.hpp>
#include <boost/smart_ptr.hpp>
#include <boost/variant.hpp>

#include <mysql.h>

#include <cppconn/driver.h>
#include <cppconn/exception.h>
#include <cppconn/resultset.h>
#include <cppconn/statement.h>
#include <cppconn/prepared_statement.h>

#include "../globals/globals.hpp"
#include "../exception/cassandra_exception.hpp"

struct mapCompare
{
    bool operator()(char const * a, char const * b)
    {
        return (strcmp(a, b) < 0);
    }
};

typedef map<const char*, boost::variant<short, int, long double, char*>, mapCompare > TableFieldsMap;

struct DataSet
{
    unsigned int rowCount;
    boost::shared_array<TableFieldsMap>data;
};

class Database
{
private:
    MYSQL *connection;

public:
    Database();
    ~Database();

    bool connect(const char *, const char *, const char *, const char *);
    DataSet select(const char*);
};

#endif
  • 和代码:

    #include "database.hpp"
    Database::Database()
    {
        DLOG(INFO) << "::Database";
    }
    
    Database::~Database()
    {
        DLOG(INFO) << "~Database";
    
        if (connection) mysql_close(connection);
    }
    
    bool Database::connect(const char* server, const char* user, const char* password, const char* database)
    {
        DLOG(INFO) << ">> connect";
    
        connection = mysql_init(NULL);
    
        if (!connection)
        {
            cout << "Error" << endl;
            return (false);
        }
    
        if (!mysql_real_connect(connection, server, user, password, database, 0, NULL, 0))
        {
            cout << "Database init error (" << mysql_error(connection) << ")" << endl;
        }
    
        if (!connection)
        {
            cout << "Connection Failed!" << endl;
            return (false);
        }
    
        return (true);
    }
    
    DataSet Database::select(const char* query)
    {
        DLOG(INFO) << ">> select";
    
        MYSQL_RES *resultSet;
        MYSQL_ROW row;
        unsigned int fieldCount;
        MYSQL_FIELD* field;
    
        // Query database.
        if (mysql_query(connection, query)) throw MyException(string("Database select error (") + mysql_error(connection) + ")");
    
        // Handle result-set.
        resultSet = mysql_store_result(connection);
        if (resultSet == NULL) throw MyException(string("Database select error. Result-set is empty (") + mysql_error(connection) + ")");
    
        // Initialize return array.
        unsigned int rowCount = mysql_num_rows(resultSet);
        boost::shared_array<TableFieldsMap> retArray(new TableFieldsMap[rowCount]);
    
        // Obtain field names and types.
        fieldCount = mysql_num_fields(resultSet);
        boost::shared_array<const char *> fieldNameArray(new const char* [fieldCount]);
        boost::scoped_array<enum_field_types> fieldTypeArray(new enum_field_types[fieldCount]);
        unsigned int fieldIndex = 0;
        while ((field = mysql_fetch_field(resultSet)))
        {
            fieldNameArray[fieldIndex] = field -> name;
            fieldTypeArray[fieldIndex++] = field -> type;
        }
    
        // Fix?
        string tmpIndex;
    
        // Load data from result-set.
        unsigned int rowIndex = 0;
        while ((row = mysql_fetch_row(resultSet)) != NULL)
        {
            // Fields of the row.
            for (unsigned int i = 0; i < fieldCount; i++)
            {
                if (row[i] == NULL) throw MyException("Database error. Null.");
    
                switch (fieldTypeArray[i])
                {
                    // Medium int & Integer.
                    case MYSQL_TYPE_INT24 :
                    case MYSQL_TYPE_LONG :
                    retArray[rowIndex][fieldNameArray[i]] = boost::lexical_cast<int>(row[i]);
                    break;
    
                    case MYSQL_TYPE_DOUBLE :
                    try
                    {
                    retArray[rowIndex]["value"] = boost::lexical_cast<long double>(row[i]); // This works.
                    }
            catch (boost::bad_lexical_cast exception)
                    {
                        DLOG(ERROR) << "Error reading the phi table from database. Cast error. (" << rowIndex << ", " << row[i] << ")" << endl;
                        throw MyException("Error reading the phi table from database. Cast error.");
            }
    
                    // tmpIndex = string(fieldNameArray[i]);
                    // This doesn't work: retArray[rowIndex][fieldNameArray[i]] = boost::lexical_cast<long double>(row[i]);
                    // This doesn't work either: retArray[rowIndex][tmpIndex.c_str()] = boost::lexical_cast<long double>(row[i]);
                    break;
    
                case MYSQL_TYPE_VARCHAR :               // 15
                case MYSQL_TYPE_VAR_STRING :            // 253
                case MYSQL_TYPE_STRING :                // 254
                retArray[rowIndex][fieldNameArray[i]] = row[i];
                break;
    
                    case MYSQL_TYPE_DECIMAL :               // 0
                    case MYSQL_TYPE_TINY :                  // 1
                    case MYSQL_TYPE_SHORT :                 // 2
                    case MYSQL_TYPE_FLOAT :                 // 4
                    case MYSQL_TYPE_NULL :                  // 6
                    case MYSQL_TYPE_TIMESTAMP :             // 7
                    case MYSQL_TYPE_LONGLONG :              // 8
                    case MYSQL_TYPE_DATE :                  // 10
                    case MYSQL_TYPE_TIME :                  // 11
                    case MYSQL_TYPE_DATETIME :              // 12
                    case MYSQL_TYPE_YEAR :                  // 13
                    case MYSQL_TYPE_NEWDATE :               // 14
                    case MYSQL_TYPE_BIT :                   // 16
                    case MYSQL_TYPE_NEWDECIMAL :            // 246
                    case MYSQL_TYPE_ENUM :                  // 247
                    case MYSQL_TYPE_SET :                   // 248
                    case MYSQL_TYPE_TINY_BLOB :             // 249
                    case MYSQL_TYPE_MEDIUM_BLOB :           // 250
                    case MYSQL_TYPE_LONG_BLOB :             // 251
                    case MYSQL_TYPE_BLOB :                  // 252
                    default:
                    DLOG(FATAL) << "Database error. Unsupported datatype";
                    throw MyException("Database error. Unsupported datatype");
                    break;
                }
            rowIndex++;
            }
        }
    
        mysql_free_result(resultSet);
    
        DataSet retStruct;
        retStruct.rowCount = rowCount;
        retStruct.data = retArray;
    
        return (retStruct);
    }
    

...

下面是调用方法:

    include "My_database.hpp"

    MyDatabase::MyDatabase(string server, string user, string password, string database)
    {
        DLOG(INFO) << "::MyDatabase (1)";

        init(server.c_str(), user.c_str(), password.c_str(), database.c_str());
    }

    MyDatabase::MyDatabase(const char* server, const char* user, const char* password, const char* database)
    {
        DLOG(INFO) << "::MyDatabase (2)";

        init(server, user, password, database);
    }

    MyDatabase::~MyDatabase()
    {
        DLOG(INFO) << "~MyDatabase";
    }

    void MyDatabase::init(const char* server, const char* user, const char* password, const char* database)
    {
        DLOG(INFO) << ">> init";

        connect(server, user, password, database);
    }

    boost::shared_array<long double>MyDatabase::loadPhiTable()
    {
        DLOG(INFO) << ">> LoadPhiTable";

        // Select from database.
        DataSet selectData = select("SELECT value FROM phi");
        boost::shared_array<TableFieldsMap>phiArray = selectData.data;
        unsigned int rowCount = selectData.rowCount;

        // Initialize return array.
        boost::shared_array<long double> retArray(new long double[rowCount]);

        unsigned int i = 0;
        try
        {
            for (i = 0; i < rowCount; i++) retArray[i] = boost::get<long double>(phiArray[i]["value"]);
        }
        catch (boost::bad_get exception)
        {
            throw MyException(string("Error reading the phi table. Conversion error. (index: ") + boost::lexical_cast<string>(i) + ", value: " + boost::lexical_cast<string>(phiArray[i]["value"]) + ")");
        }

        return (retArray);
    }

任何其他建议也将不胜感激。我的第一个 C++ 程序。

4

1 回答 1

4

问题是

boost::shared_array<const char *> fieldNameArray = ...;

不拥有数组的元素,因此当间接数据消失时,它们将作为悬空指针留下。mysql_fetch_field如果间接返回的指针指向池数据,这可能发生在 MySQL API 中。

最简单的解决方法是更改fieldNameArray​​为包含std::string.

于 2012-06-11T12:03:55.367 回答