Forward reference avec une référence

a marqué ce sujet comme résolu.

Bonjour,

Je lis un livre pour apprendre Qt 5 avec C++14. J’essaye d’adapter un exemple mais je suis complètement bloqué. J’essaye de faire un singleton DatabaseManager avec des data access object publique. Je ne sais pas trop comment expliquer, tout ça est nouveau pour moi, voici les codes :

DatabaseManager.h

#include <QString>
#include <memory>
#include "Dao.h"


class QSqlDatabase; // Forward declaration
const QString DATABASE_FILENAME{ "database.db" };

class DatabaseManager
{
public:
    static DatabaseManager& get();
    ~DatabaseManager();

    const Dao dao;

protected:
    DatabaseManager(const QString& path = DATABASE_FILENAME);
    DatabaseManager& operator=(const DatabaseManager& rhs);

private:
    QSqlDatabase* database_;
};

DatabaseManager.cpp

#include "DatabaseManager.h"
#include <QSqlDatabase>

DatabaseManager& DatabaseManager::get()
{
    static DatabaseManager singleton;
    return singleton;
}

DatabaseManager::DatabaseManager(const QString& path)
    : database_(new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE")))
    , dao(*database_)
{
    database_->setDatabaseName(path);
    database_->open();

    dao.init();
}

DatabaseManager::~DatabaseManager()
{
    database_->close();
    delete database_;
}

Dao.h

class QSqlDatabase;

class Dao
{
public:
    AirplaneDao(QSqlDatabase& database);
    void init() const;
    
public:
    QSqlDatabase& database_;
};

Dao.cpp

#include <QSqlDatabase>
#include <QSqlQuery>
#include <QVariant>

#include "DatabaseManager.h"

Dao::Dao(QSqlDatabase& database)
    : database_(database)
{
}

void Dao::init() const
{
    bool a = database_.tables().contains("images");

    if (!a) {
        QSqlQuery query{ database_ };
        query.exec("...");
    }
}

Je suis désolé pour la longueur du code. D’après mon debugger, Dao::database_ ne contient absolument rien et la ligne bool a = database_.tables().contains("images"); de Dao.cpp produit une Segmentation fault. Si je place la même ligne dans le constructeur de DatabaseManager, aucun problème, ça fonctionne.

Je me doute bien que je me suis embrouillé entre les * et les &. Mon objectif est de pouvoir faire :

DatabaseManger::get().dao.create(<params>);
Object a{ DatabaseManager::get().dao.last() };

Je vous remercie pour votre aide ! :)

Construction paresseuse et présence d’un constructeur avec paramètre sont totalement antinomique. Il va falloir sacrifier la construction paresseuse.

Une vielle diatribe à l’égard des singletons: https://openclassrooms.com/forum/sujet/du-singleton-18995

Après, l’autre problème: active les warnings. Peu importe ce que l’on met dans la liste d’initialisation, le seul ordre qui importe, c’est celui de déclaration des variables membres dans la définition de la classe. C’est pour cela que ta référence est prise sur quelque chose qui n’existe pas encore.

PS: pas besoin de pointeur si database_ est créée dans le constructeur et détruite dans le destructeur — instance > unique_ptr » raw ptr.

C’est quel livre ? Je suis un peu surpris, certaines syntaxes ne font pas très C++14 (par exemple le pointeur nu ou le constructeur par copie en protected)

get() -> instance()

En général, on n’aime pas les fonctions init, c’est le rôle des constructeurs.

En termes de conception, tu veux faire un DAO, mais ta classe Dao est clairement spécifique a une base de données (crée par DatabaseManager, prend un base de données en paramètres, la Dao qui initialise la base de données, etc). C’est pas une bonne abstraction de DAO et ca ne me semble pas tres coherent.

Si tu veux un exemple de DOA, tu peux regarder la classe QAbstractItemModel.

+0 -0

Construction paresseuse et présence d’un constructeur avec paramètre sont totalement antinomique. Il va falloir sacrifier la construction paresseuse.

Qu’entends-tu par construction paresseuse ?

Après, l’autre problème: active les warnings. Peu importe ce que l’on met dans la liste d’initialisation, le seul ordre qui importe, c’est celui de déclaration des variables membres dans la définition de la classe. C’est pour cela que ta référence est prise sur quelque chose qui n’existe pas encore.

En effet ça règle le soucis. Mais il semblerait que j’ai un problème plus global.. ;)

PS: pas besoin de pointeur si database_ est créée dans le constructeur et détruite dans le destructeur — instance > unique_ptr » raw ptr.

C’est pas justement le principe du forward declaration ? Sans le pointeur ou la référence, le code ne compile pas car le type n’est pas défini encore.

C’est quel livre ? Je suis un peu surpris, certaines syntaxes ne font pas très C++14 (par exemple le pointeur nu ou le constructeur par copie en protected)

Mastering QT 5 - Second edition aux éditions Packt. C’est loin d’être un bon livre mais il permet d’apprendre QT.

En termes de conception, tu veux faire un DAO, mais ta classe Dao est clairement spécifique a une base de données (crée par DatabaseManager, prend un base de données en paramètres, la Dao qui initialise la base de données, etc). C’est pas une bonne abstraction de DAO et ca ne me semble pas tres coherent.

En fait ma classe Dao est spécifique à une table et j’ai 2 autres classes qui sont spécifiques à d’autre tables également accessible par le DataManager::get().

Je vois bien que l’organisation de mon code ne convient pas mais je ne vois pas comment faire autrement… J’ai besoin d’un accès à ma base de donnée dans tout le code afin de pouvoir effectuer des requêtes simplement. Quel est une bonne façon de faire ?

Merci à vous

Mastering QT 5 - Second edition aux éditions Packt. C’est loin d’être un bon livre mais il permet d’apprendre QT.

Wizix

Je vois de voir que dans la première édition, il est mentionné explicitement "C++14", mais que dans la seconde, il est juste écrit "C++". A mon avis, il faut voir ce livre avant tout comme un livre sur Qt, pas du tout sur le C++14.

Mais bon, ce n’est qu’un détail en fait. (Je ne l’ai pas lu)

En fait ma classe Dao est spécifique à une table et j’ai 2 autres classes qui sont spécifiques à d’autre tables également accessible par le DataManager::get().

Je vois bien que l’organisation de mon code ne convient pas mais je ne vois pas comment faire autrement… J’ai besoin d’un accès à ma base de donnée dans tout le code afin de pouvoir effectuer des requêtes simplement. Quel est une bonne façon de faire ?

Wizix

A mon avis, le problème est le même que pour beaucoup de monde : penser ses classes en termes de structures de données ("qu’est ce que mes classes contiennent ?") et pas de services rendus ("quels services doivent rendre mes classes ?").

Et ce que je recommande en général pour corriger cela, c’est l’approche TDD (test driven development). Tu écris les tests avant les fonctions. Le but est de se forcer a penser en termes de code utilisateur et pas d’implementation.

L’idée en gros, c’est de suivre la démarche suivante :

  • qui utilisent ton DAO ? (= définir les besoins)
  • quelle syntaxe pour utiliser ton DAO, coté client ? (= definir l’interface public de ton DAO)
  • ecrire les tests
  • écrire l’implémentation de ton DOA

Et faire cela de facon recursive. Par exemple, si tu as une classe Client qui utilise ta classe Dao et que ton DAO utilise une classe SqlDatabase, alors tu fais une première fois ce boulot sur Client pour écrire tes tests sur Dao (en ignorant totalement SqlDatabase). Puis tu fais pareil sur Dao (en ignorant totalement Client) pour écrire tes tests sur SqlDatabase.

+0 -0

Construction paresseuse et présence d’un constructeur avec paramètre sont totalement antinomique. Il va falloir sacrifier la construction paresseuse.

Qu’entends-tu par construction paresseuse ?

Retarder la construction au premier appel à getinstance(), et c’est totalement incompatible avec la présence d’un constructeur à paramètre vu que getinstance ne peut pas prendre de paramètres…

Cf le lien que j’avais donné pour contourner.

PS: pas besoin de pointeur si database_ est créée dans le constructeur et détruite dans le destructeur — instance > unique_ptr » raw ptr.

C’est pas justement le principe du forward declaration ? Sans le pointeur ou la référence, le code ne compile pas car le type n’est pas défini encore.

Ici, il faudrait effectivement introduire une dépendance forte. Perso, je préfère ça à la manipulation de pointeurs bruts.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte