Hi,

I am trying to create a vector of a class which contains ofstream object as one of its member. The problem I am facing is that when I add single element to the vector, it work fine. As soon as I add another element to the vector, it changes the address of the ofstream object of the previously added element. As the address changes, I can not write to the file pointed to by that ofstream object. The example program in which I am just printing the elements value after adding two elements to the vector is given below:

#include<iostream>
#include<vector>
#include <fstream>
using namespace std;

class routecaseBasedCDRFile
{
  public:
        routecaseBasedCDRFile(const routecaseBasedCDRFile& obj);
        routecaseBasedCDRFile() ;
        ~routecaseBasedCDRFile() ;
        routecaseBasedCDRFile& operator=(const routecaseBasedCDRFile& rhs);

        string routecaseName;
        string myRouteCaseBasedFilename;
        bool myRouteCaseBasedFileOpen;

        unsigned long  myRouteCaseBasedCDRFileSequenceNumber;
        long myCurrentNumOfRecordsWrittenInRouteCaseBasedFile;

        ofstream myRouteCaseBasedOutCDRfile; // This is the output file

        bool isRouteCaseBasedFileOpen() {}
        void closeRouteCaseBasedFile() {}
        void checkRouteCaseBasedFileTimeLimit() {}
        int openNewRouteCaseBasedFile() {}
        //int openNewRouteCaseBasedFile(CallDetailRecord &cdr);

};
routecaseBasedCDRFile::routecaseBasedCDRFile(const routecaseBasedCDRFile &obj)
{
  cout<<"routecaseBasedCDRFile::routecaseBasedCDRFile(const routecaseBasedCDRFile &obj)"<<endl;
 *this = obj;
}

routecaseBasedCDRFile::routecaseBasedCDRFile()
{
  cout<<"routecaseBasedCDRFile::routecaseBasedCDRFile()"<<endl;
}

routecaseBasedCDRFile::~routecaseBasedCDRFile()
{
  cout<<"routecaseBasedCDRFile::~routecaseBasedCDRFile()"<<endl;
}

routecaseBasedCDRFile& routecaseBasedCDRFile::operator=(const routecaseBasedCDRFile& rhs)
{
 cout<<"routecaseBasedCDRFile::operator=(const routecaseBasedCDRFile& rhs)"<<endl;
 routecaseName = rhs.routecaseName;
 myRouteCaseBasedFilename = rhs.myRouteCaseBasedFilename;
 myRouteCaseBasedFileOpen = rhs.myRouteCaseBasedFileOpen;
 myrouteCaseBasedcdrSeqNumber = rhs.myrouteCaseBasedcdrSeqNumber;
 myRouteCaseBasedCDRFileSequenceNumber = rhs.myRouteCaseBasedCDRFileSequenceNumber;
 myCurrentNumOfRecordsWrittenInRouteCaseBasedFile = rhs.myCurrentNumOfRecordsWrittenInRouteCaseBasedFile;


}


int main()
{
  vector<routecaseBasedCDRFile> routecaseFileVector;
  routecaseBasedCDRFile routecaseCDRFile1,routecaseCDRFile2;

  routecaseCDRFile1.routecaseName = "no_routecase";
  routecaseCDRFile1.myrouteCaseBasedcdrSeqNumber = 0;
  routecaseCDRFile1.myRouteCaseBasedFileOpen = false;
  routecaseCDRFile1.myCurrentNumOfRecordsWrittenInRouteCaseBasedFile = 0;
  routecaseCDRFile1.myRouteCaseBasedCDRFileSequenceNumber = 0;
  routecaseFileVector.push_back(routecaseCDRFile1);

  for(vector<routecaseBasedCDRFile>::iterator i=routecaseFileVector.begin();i != routecaseFileVector.end();i++)
  {
        if((*i).routecaseName == "no_routecase")
        {
                (*i).myRouteCaseBasedFilename = "/tmp/file1.txt";
                (*i).myRouteCaseBasedOutCDRfile.unsetf(ios::unitbuf);
                (*i).myRouteCaseBasedOutCDRfile.rdbuf()->pubsetbuf(0,2000000);
                (*i).myRouteCaseBasedOutCDRfile.open((*i).myRouteCaseBasedFilename.c_str());
        }
  }

 for( vector<routecaseBasedCDRFile>::iterator j=routecaseFileVector.begin();j != routecaseFileVector.end();j++)
     {
       cout <<"PRINT 1::(*j).myRouteCaseBasedOutCDRfile is "<<(*j).myRouteCaseBasedOutCDRfile <<endl;
       cout <<"PRINT 1::(*j).routecaseName is "<<(*j).routecaseName <<endl;
       cout <<"PRINT 1::(*j).myRouteCaseBasedFileOpen is "<<(*j).myRouteCaseBasedFileOpen <<endl;
       cout <<"PRINT 1::(*j).myRouteCaseBasedFilename is "<<(*j).myRouteCaseBasedFilename <<endl;
     }


  routecaseCDRFile2.routecaseName = "no_routecase_1";
  routecaseCDRFile2.myrouteCaseBasedcdrSeqNumber = 0;
  routecaseCDRFile2.myRouteCaseBasedFileOpen = false;
  routecaseCDRFile2.myCurrentNumOfRecordsWrittenInRouteCaseBasedFile = 0;
  routecaseCDRFile2.myRouteCaseBasedCDRFileSequenceNumber = 0;
  routecaseFileVector.push_back(routecaseCDRFile2);

  for(vector<routecaseBasedCDRFile>::iterator i=routecaseFileVector.begin();i != routecaseFileVector.end();i++)
  {
        if((*i).routecaseName == "no_routecase_1")
        {
                (*i).myRouteCaseBasedFilename = "/tmp/file2.txt";
                (*i).myRouteCaseBasedOutCDRfile.unsetf(ios::unitbuf);
                (*i).myRouteCaseBasedOutCDRfile.rdbuf()->pubsetbuf(0,2000000);
                (*i).myRouteCaseBasedOutCDRfile.open((*i).myRouteCaseBasedFilename.c_str());
        }
  }

 for( vector<routecaseBasedCDRFile>::iterator j=routecaseFileVector.begin();j != routecaseFileVector.end();j++)
     {
       cout <<"PRINT 2::(*j).myRouteCaseBasedOutCDRfile is "<<(*j).myRouteCaseBasedOutCDRfile <<endl;
       cout <<"PRINT 2::(*j).routecaseName is "<<(*j).routecaseName <<endl;
       cout <<"PRINT 2::(*j).myRouteCaseBasedFileOpen is "<<(*j).myRouteCaseBasedFileOpen <<endl;
       cout <<"PRINT 2::(*j).myRouteCaseBasedFilename is "<<(*j).myRouteCaseBasedFilename <<endl <<endl;

     }

  return 0;
}

The value of myRouteCaseBasedOutCDRfile of the first element of the vector comes out to be different before and after adding the second element. I am not able to understand the reason behind it. And because of changed value, I can not access the file pointed to by it.

Any help would be useful.

Recommended Answers

All 2 Replies

std::ofstream is not a CopyConstructible or CopyAssignable type. But it is MoveConstructible and MoveAssignable.
The CopyConstructor is explicitly deleted.
http://en.cppreference.com/w/cpp/io/basic_ofstream/basic_ofstream
The CopyAssignment operator is implicitly deleted.
http://en.cppreference.com/w/cpp/io/basic_ofstream/operator%3D

The CopyAssignment operator of routecaseBasedCDRFile wold have been implicitly deleted if had not been declared. In this case, there is a user-defined CopyAssignment operator which does not assign to the std::ofstream member, but copies the state of all the other members, and therefore leaves the object in an invalid state.

Tip: Rule of zero http://en.cppreference.com/w/cpp/language/rule_of_three

#include <iostream>
#include <fstream>
#include <fstream>
#include <vector>
#include <type_traits>

struct A // MoveConstructible, MoveAssignable, not CopyConstructible, not CopyAssignable
{
    explicit A( std::string path ) : file(path) {}
    A( std::ofstream&& stm ) : file( std::move(stm) ) {} // transfer ownership of the stream

    void test_it() { file << "output from A #" << id << '\n' ; }

    std::ofstream file ; // std::ofstream is Moveable, but it is not Copyable
    // copy constructor of A is implicitly deleted
    // move constructor of A is implicitly defaulted
    // copy assignment of A is implicitly deleted
    // move assignment of A is implicitly defaulted

    int id = seq_no++ ;
    static int seq_no ;
};

int A::seq_no = 0 ;

int main()
{
    std::cout << std::boolalpha
              << "MoveConstructible? " << std::is_move_constructible<A>::value << '\n'
              << "CopyConstructible? " << std::is_copy_constructible<A>::value << '\n'
              << "MoveAssignable? " << std::is_move_assignable<A>::value << '\n'
              << "CopyAssignable? " << std::is_copy_assignable<A>::value << "\n\n" ;

    std::vector<A> seq ;

    seq.emplace_back( "out0.txt" ) ;
    seq.push_back( std::ofstream( "out1.txt" ) );

    std::ofstream file( "out2.txt" ) ;
    seq.emplace_back( std::move(file) ) ;

    for( A& a : seq ) a.test_it() ;

    A object( "out3.txt" ) ;

    // seq.push_back( object ); // *** error: call to implicitly-deleted copy constructor of 'A'
                                //      note: ... implicitly deleted because field 'file' has a deleted copy constructor

    seq.push_back( std::move(object) ); // MoveConstructible
    seq.back().test_it() ;

    seq.back() = A( "out4.txt" ) ; // MoveAssignable
    seq.back().test_it() ;

    A object2( "out5.txt" ) ;

    // seq.front() = object2 ; // *** error: ... copy assignment operator is implicitly deleted 
                               //      note: ... implicitly deleted because field 'file' has a deleted copy assignment operator

    seq.front() = std::move(object2) ; // MoveAssignable
    seq.front().test_it() ;

    using std::swap ;
    swap( seq.front(), seq.back() ) ; // Moveable
    for( A& a : seq ) a.test_it() ;
}

http://coliru.stacked-crooked.com/a/702f147c26698b19

Part of the practical upshot of what vijayan121 is saying here is that you should always pass stream objects (or any other non-copyable objects) by explicit reference (that is, using the reference operator, &) rather than by a pointer reference or by value.

Actually, this is a gross oversimplification, but it is the easiest rule to follow until you have a better understanding of the actual mechanics involved (see the Abelson example at the end of the 'Real Life' section on that page for a clear explanation of why this sort of approach can be useful in a CS context).

On a related note, you will notice that the code example does not use the using namespace directive. This is an important issue, for a number of reasons, but the main one is namespace control. As a general rule the using namespace directive should be avoided in any but the simplest programs; the main purpose of namespaces is to prevent name collisions between different modules, and the using namespace directive subverts that goal. It is better as a rule to either explicitly scope cross-namespace references using the scoping operator, as vijayan121 does here, or to scope the individual elements you are using, like so:

using std::cout;

Explicit scoping is always preferred, but since it can get awkward, the element-specific using directive is acceptable, in most cases, as long as it isn't abused (which is something of a judgement call). This is especially true for header files - you should never have a using or using namespace directive in a header file, as it would get propagated to every file that includes it, which can lead to unexpected namespace collisions.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.