jeudi 9 novembre 2023

C++ Program Keeps Crashing Without Specific Error [closed]

I am trying to implement an Observer/Subject design pattern between multiple classes. My compiler keeps returning to me the following:

[Done] exited with code=3221225477 in 0.603 seconds.

I assumed this was a success, until I realized nothing worked. I found out (through commenting and uncommenting code) that the crash occurs in 2 places, either when I try to attach an Observer to a child class of Subject, or when I try to call the notify method (which in turn updates the observer). Furthermore, as you will see, the LogObserver default constructor (Child class of Observer), should create and open a file called "gamelog.txt", but fails to do even that. I am not receiving any specific error message, so I dont know why it is crashing or where it is going wrong. Any and all help debugging this?

I'll show the code for the relevant .h and cpp files here, what I expected was for a file to be created, for the observer to be attached to OrderList (child class of Subject), and finally for the text file to be written to upon adding and order to orderlist (which should trigger the Notify method).

Here is the main driver:

#include "LoggingObserver.h"
#include "Orders.h"



void testLoggingObserver(){
    //3 main issues
        //1: File is NOT created upon logObserver default constructor
        //2: The Attach method seems to crash the program
        //3: Keep receiving error for "unidentified reference" to methods 

    //create instances of classes
    LogObserver* logObserver;
    OrdersList * orderList;

    //CRASHES HERE
    //Add the observer to the subjects
    orderList->Attach(logObserver);

    //CRASHES HERE
    orderList->addOrder(new DeployOrder());
}

int main(){
    
    testLoggingObserver();
    cout<<"LogObserver Default Here!"<<endl;
    return 0;
}

Here is the Header for LoggingObserver:

#pragma once
#include <iostream>
#include <vector>
#include <fstream>
#include <set>
#include <algorithm>
#include <list>
#include <iterator>
#include <string>

using namespace std;

//An Interface ILoggable. This interface will be inherited by all classes that can be the subject
//of the logging mechanism
#ifndef I_LOGGABLE_HPP
#define I_LOGGABLE_HPP
class ILoggable{
    public:
        //virtual ~ILoggable();
        //Will create and return a string to be output to the log file
        virtual string stringToLog()=0;
};
#endif

//#ifndef SUBJECT
//#define SUBJECT
class Observer;

//Subject Class will have a list of Observers and methods to add/remove and notify observers
class Subject{
    private:
        //list of Observers
        list<Observer*> *observers;

    public:
        //default contructor and destructor
        //Subject();
        //~Subject();
        
        //Methods to add, remove, and notify Observers
        virtual void Attach(Observer* o)=0;
        virtual void Detach(Observer* o)=0;
        virtual void Notify()=0;

        //friend classes
        friend class GameEngine;
        friend class OrdersList;
        friend class Order;
        friend class Command;
        friend class CommandProcessor;
};
//#endif

//#ifndef OBSERVER
//#define OBSERVER
//Observer Class will have a method to update the state of the observer
class Observer{
    private:

    public:
        //destructor
        ~Observer();
        //method to update state of observer
        virtual void Update(Subject* subject) = 0;
    
    protected:
        //default constructor
        //Observer();
};
//#endif


//#ifndef LOG_OBSERVER
//#define LOG_OBSERVER
class GameEngine;


//This class will override the update method to write the state of the subject to the log file
class LogObserver: public Observer{
    private:
        //file to write to
        ofstream logFile;
        
        //variables attributed to phase
        string playerName;
        string phaseName;
        //GameEngine* subjectGameEngine;

        //variables attributed to game statistics
        int totalContinents;
        bool winnerStatus;
        string conqueredContinent;

    public:
        LogObserver();
        //LogObserver(GameEngine* aSubjectEngine);
        ~LogObserver();
        //update method
        void Update(Subject* subject) override;

        //To display player information and current pahse information
        void logToGameLog(const string& logEntry);

};
//#endif

Here is the .cpp for LoggingObserver:

#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <list>
#include <algorithm>

#include "LoggingObserver.h"

using std::vector;
using std::list;

//Below we will implement all the abstract functions we declared in the previous LogginObserver.h Header File

//========================================ILoggable Class======================================
//Default constructor



//========================================Subject Class======================================
//Default constructor
/*
Subject::Subject(){
    observers = new list<Observer*>;
}


//destructor
Subject::~Subject(){
    delete observers;
}
*/
/*
//Implementation of Add method (adds an observer to the observers list)
void Subject::Attach(Observer* o){
    observers->push_back(o);
}

//Implementation of Remove method (removes an observer from the observers list)
void Subject::Detach(Observer* o){
    observers->remove(o);
}

//Implementation of the Notify method (will call on the observers update method)
void Subject::Notify(){
    list<Observer*>::iterator i = observers->begin();
    for(;i != observers->end(); ++i){
        //using "this" to pass the subject to the update method
        (*i)->Update(this);
    }
}
*/

//========================================Observer Class======================================
//Default constructor
Observer::~Observer(){
    
}

//========================================LogObserver Class======================================
//Default constructor (will open file)
LogObserver::LogObserver(){
    logFile.open("gamelog.txt", ios::app);
    cout<<"LogObserver Default Here!"<<endl;
}

//Destructor
LogObserver::~LogObserver(){
    logFile.close();
}


//Overriding the update method from Observer (will grab relevant information and update file)
void LogObserver::Update(Subject* subject){
    ILoggable* loggable = dynamic_cast<ILoggable*>(subject);
    if(loggable){
        string logEntry = loggable -> stringToLog();
        logToGameLog(logEntry);
    }
}

//implementing loToGameLog function (will write to file)
void LogObserver::logToGameLog(const string& logEntry){
    logFile << logEntry << endl;
}

Here is the .h for Orders:

#pragma once
#include <vector>
#include <string>
#include <iostream>
#include "LoggingObserver.h"



class Order : public ILoggable
{
    public:

        // default constructor
        Order();

        // constructor with order type
        Order(std::string orderType);

        // copy constructor
        Order(Order &o);

        // Overload the << operator to describe the order
        friend std::ostream & operator << (std::ostream& os, Order& order);

        // checks if an order is valid
        bool validate();

        // executes an order
        void execute();

        bool getIsExecuted();

        void setIsExecuted(bool isExecuted);

        std::string getOrderType();

        void setOrderType(std::string orderType);

        // override the assignment operator
        Order& operator=(Order& o);

        //Overriden stringToLog() method fromILoggable
        string stringToLog() override;


    private:

        std::string orderType;

        bool isExecuted;

};

// specific order classes:
class DeployOrder : public Order 
{
    public:
        // default constructor
        DeployOrder();
};
class AdvanceOrder :public  Order 
{
    public:
        // default constructor
        AdvanceOrder();
};
class BombOrder : public Order 
{
    public:
        // default constructor
        BombOrder();
};
class BlockadeOrder : public Order 
{
    public:
        // default constructor
        BlockadeOrder();
};
class AirliftOrder : public Order 
{
    public:
        // default constructor
        AirliftOrder();
};
class NegotiateOrder : public Order 
{
    public:
        // default constructor
        NegotiateOrder();
};


class OrdersList : public Subject
{

    private:
        std::vector<Order *> orders;

    public:

        // default constructor
        OrdersList();

        // copy constructor
        OrdersList(OrdersList &ol);

        // destructor
        ~OrdersList();

        std::vector<Order *> getOrders();

        Order* createOrder(std::string orderType);

        // add an order to the list
        void addOrder(Order *order);

        // change the position of an order in the order list
        void move(int listPosition1, int targerList);

        // remove an order from the order list
        void remove(int listPosition);

        // prints all the orders in the list
        void printOrdersList();
        
        // Overload the << operator to describe the order list
        friend std::ostream& operator<<(std::ostream& os, OrdersList& ordersList);
        
        // override the assignment operator
        OrdersList& operator=(OrdersList& ol);
        void Attach(Observer* o) override;
        void Detach(Observer* o) override;
        void Notify() override;
};

inline void testOrdersLists()
{
    using namespace std;
    // create an order list and add every type of oder to it
    OrdersList * ol = new OrdersList();
    ol->createOrder("Deploy");
    ol->createOrder("Advance");
    ol->createOrder("Bomb");
    ol->createOrder("Blockade");
    ol->createOrder("Airlift");
    ol->createOrder("Negotiate");

    // print all the elements in the order list
    cout << "\nInitial Order List:\n" << endl;
    cout << *ol;

    // 'execute' an order
    cout << "\n";
    ol->getOrders().front()->execute();

    // remove an order
    cout << "\n";
    ol->remove(2);

    // print all the elements in the new order list
    cout << "\nRemoved Order 2 from list:\n" << endl;
    cout << *ol;

    // move an order
    cout << "\n";
    ol->move(1,2);

    // print all the elements in the new order list
    cout << "\nChanged the first order to be the seconds order:\n" << endl;
    cout << *ol;

}

And here is the .cpp for orders:

#include <iostream>
#include <vector>
#include <string>
#include "Orders.h"
#include "LoggingObserver.h"


// list<Order *> orders = ol->getOrders(); // Make a copy of the list to avoid iterator issues

using namespace std;

// default order constructors:

// generic order
Order::Order(){
    setOrderType("Generic");
    setIsExecuted(false);
}

// order with type
Order::Order(string orderType){
    this->setOrderType(orderType);
    setIsExecuted(false);
}

//Overriden stringToLog() method fromILoggable
string Order::stringToLog() {
    string log = "Observing Issueing Orders Phase information is: \n Orders: " + orderType;
    return log;
}

// deploy order
DeployOrder::DeployOrder()
: Order("Deploy") {}

// advance order
AdvanceOrder::AdvanceOrder()
: Order("Advance") {}

// bomb order
BombOrder::BombOrder()
: Order("Bomb") {}

// blockade order
BlockadeOrder::BlockadeOrder()
: Order("Blockade") {}

// airlift order
AirliftOrder::AirliftOrder()
: Order("Airlift") {}

// negotiate order
NegotiateOrder::NegotiateOrder()
: Order("Negotiate") {}


// copy constructor
Order::Order(Order &o)
{
    setOrderType(o.getOrderType());
}

string Order::getOrderType() { return orderType; }

void Order::setOrderType(string type) { orderType = type; }

bool Order::getIsExecuted() { return isExecuted; } 

void Order::setIsExecuted(bool isExecuted) { this->isExecuted = isExecuted; }


bool Order::validate()
{
    return true;
}

void Order::execute()
{
    if(validate())
    {
        cout << "Executing " << this->orderType << std::endl;
        setIsExecuted(true);
    }
}

// stream operator order
std::ostream & operator << (std::ostream& os, Order & order)
{
    os << "This is a " << order.getOrderType() << " order. ";

    if(order.getIsExecuted())
    {
        os << "Order was executed";
    }
    os << std::endl;

    return os;
}

// assignment operater order
Order& Order::operator=(Order& o)
{ 
    o.setOrderType(getOrderType());
    o.setIsExecuted(getIsExecuted());
    return *this;
} 

// Order list:

// default constructor
OrdersList::OrdersList(){
    cout<<"OrderList Constructer here!"<<endl;
}

// destructor
OrdersList::~OrdersList()
{
    for (Order *order : orders) {
        delete order;
    }
    orders.clear();
}

// copy constructor
OrdersList::OrdersList(OrdersList &ol)
{
    this->orders = ol.getOrders();
}


// stream operator order list
ostream& operator<<(ostream& os, OrdersList & ol)
{
    int i = 0;
    for (Order *order : ol.getOrders()) 
    {
        os << ++i << ": ";
        os << *order ;
    }
    return os;
}

vector<Order *> OrdersList::getOrders()
{
   return this->orders;
}

Order* OrdersList::createOrder(std::string orderType)
{
    Order *newOrder;
    if(orderType == "Deploy")
    {
        newOrder = new DeployOrder();
    }

    else if(orderType == "Advance")
    {
        newOrder = new AdvanceOrder();
    }
    else if(orderType == "Bomb")
    {
        newOrder = new BombOrder();
    }
    else if(orderType == "Blockade")
    {
        newOrder = new BlockadeOrder();
    }
    else if(orderType == "Airlift")
    {
        newOrder = new AirliftOrder();
    }
    else if(orderType == "Negotiate")
    {
        newOrder = new NegotiateOrder();
    }
    else
    {
        newOrder = new Order();
    }
    this->addOrder(newOrder);
    return newOrder;
}

void OrdersList::addOrder(Order *order)
{
    orders.push_back(order);
    Notify();
}

void OrdersList::move(int initialListPosition, int targetListPosition)
{
    // verifiy that the index positions are valid
    int listSize = orders.size();
    if(initialListPosition > listSize || initialListPosition < 1 || targetListPosition > listSize || targetListPosition < 1)
    {
        cout << "Invalid move" << std::endl;
    }
    else
    {
        // remove the order
        vector<Order *>::iterator itr1 = orders.begin();
        advance(itr1,initialListPosition-1);
        Order * o = *itr1; // save a pointer to the order before removing it
        orders.erase(itr1);

        // add it back in the new index
        vector<Order*>::iterator itr2 = orders.begin();
        advance(itr2,targetListPosition-1);
        orders.insert(itr2, o);

        cout << "Order moved" << std::endl;
    }
}


void OrdersList::remove(int listPosition)
{
    
    vector<Order *>::iterator itr = orders.begin();
    advance(itr,listPosition-1);
    orders.erase(itr);

    cout << "Order removed" << std::endl;
}

void OrdersList::printOrdersList()
{
    int i = 0;
    for (Order *order : orders) 
    {
        cout << ++i << ": ";
        cout << *order ;
    }
}

// assignment operater order
OrdersList& OrdersList::operator=(OrdersList& ol)
{ 
    for (Order* order : ol.getOrders()) {
        // Assuming that Order has a copy constructor
        orders.push_back(new Order(*order));
    }
    return *this;
} 

void OrdersList::Attach(Observer* o){
    observers->push_back(o);
    cout << "im attaching"<<endl;
}

//Implementation of Remove method (removes an observer from the observers list)
void OrdersList::Detach(Observer* o){
    observers->remove(o);
}

//Implementation of the Notify method (will call on the observers update method)
void OrdersList::Notify(){
    list<Observer*>::iterator i = observers->begin();
    for(;i != observers->end(); ++i){
        //using "this" to pass the subject to the update method
        cout << "im notifying"<<endl;
        (*i)->Update(this);
    }
}

Aucun commentaire:

Enregistrer un commentaire