尝试在向量中添加元素时,为什么调用了我的复制构造函数?

问题描述

我试图在我的向量中添加一个客户,当我运行程序时,将调用复制构造函数。我正在做一个分配工作,我需要一个向量客户,并且必须能够添加客户,显示客户,查找客户以及加载/存储数据。我创建的矢量错误吗?我只是c ++的新手,真的不确定矢量。

Main.cpp中的代码

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include "Customer.h"
using namespace std;


void OutputFileStream();
void parseLine(const string& str);
void InputFileStream();
void save(vector<Customer> customers);
void load(vector<Customer>& customers);
void addCustomer(vector<Customer>& vect);
void displayAll(vector<Customer>& customers);
//void printActions();
vector<Customer> customers;

void OutputFileStream()
{
    cout << "Creating and writing to file: Customer.txt" << endl;

    ofstream outStream("customers.txt");  // write mode (overwrites existing data)

    if (outStream.good())
    {
        int customerID = 150033;
        outStream << "This is a line of text.\n";
        outStream << "This is another line of text.\n";
        outStream << "This is a line of text.\n";
        int numOfPurchases = 4;
        int purchases = 0;
        outStream << customerID << "Mr" << "Jack" << "New" << numOfPurchases << purchases << endl;
        //int *purchases = customers[0].getPurchases();
        outStream.close(); //  close file
        cout << "File written.\n" << endl;
    }
    else
        cout << "Unable to open file";
}

void parseLine(const string& str) {

    stringstream strStream(str); //create string stream from the string
   // int customerID;
    string title;
    string name;
    string type;
    //int numOfPurchases;
    //int purchases;
    string s;
   
    int customerID = 150033;
    getline(strStream,s,';');
    customerID = stoi(s);

    getline(strStream,title,';');
    getline(strStream,name,type,';');

    int numOfPurchases = 4;
    getline(strStream,';');
    numOfPurchases = stoi(s);

    int purchases = 0;
    getline(strStream,';');
    purchases = stoi(s);
    int* purchasesArray = new int[3];
    purchasesArray[0] = (purchases & (255 << 16)) >> 16;
    purchasesArray[1] = (purchases & (255 << 8)) >> 8;
    purchasesArray[2] = purchases & 255;
    for (int i = 0; i < 3; i++)
    {
        cout << purchasesArray[i];
    }

    cout << " CustomerID: " << customerID << "Title:" << title << " Name: " << name << " Type:" << type << " Number of Purchases: " << numOfPurchases << "Purchases: " << purchases << endl;
}

void InputFileStream() {
    cout << "Reading from a semi-colon delimited txt file" << endl;

    string line;
    ifstream inStream("customers.txt"); //opens file as an input file stream
    if (inStream.good()) //if the file is opened successfully and not empty
    {
        while (getline(inStream,line)) //reads line until false return
        {
            parseLine(line);

        }
        inStream.close();
    }
    else
        cout << "unable to open file or the file is empty!";
}

void save(vector<Customer> customers)
{
    ofstream out("customers.txt");
    if(out)
    {
        for (Customer& c : customers)
        {
            out << c.save();
        }
        out.flush();
        out.close();
    }
    else
    {
        cout << "Error Writing to File" << endl;
    }
}

void load(vector<Customer>& customers)
{
    ifstream in("customers.txt");
    if (in) {
        string line;
        while (!in.eof())
        {
            getline(in,line);
            if (line != "")
            {
                Customer c;
                c.parse(line);
                customers.push_back(c);
            }
        }
    }
}

void addCustomer(vector<Customer>& customers) {
    Customer customer;
    cin >> customer;
    customers.push_back(customer);
    
}

void displayAll(vector<Customer>& customers)
{
    cout << "\nvector contains:\n";
    for (Customer c : customers)
    cout << c.getCustomerID() << " " << c.getTitle() << c.getName() << c.getNumOfPurchases() << c.getPurchases() << c.getType() << endl;
    cout << endl;; //same as calling customer
}
//
//int getCustomerByPurchaseNumber(int numOfPurchases) {
//    vector<Customer> customers;
//    int pos = -1;
//    for (int i = 0; i < customers.size(); i++) {
//        if (customers.at(i).getNumOfPurchases() == numOfPurchases) {
//            return i;
//        }
//    }
//    return pos;
//}
//
//void findCustomerByPurchaseNumber(vector<Customer>& customers) {
//    vector<Customer> customers;
//    int numOfPurchases;
//    cout << "Please Enter Your Purchase Number:" << endl;
//    cin >> numOfPurchases;
//    int pos = customers.get(pos);
//    getCustomerByPurchaseNumber(numOfPurchases);
//    if (pos == -1) {
//        cout << "Number of Purchase Not Found! "  << endl;
//          return customers;
//    }
//    else {
//        cout << "Number Of Purchase Found! " << endl;
//
//        return customers* = customers;
//    }
//    
//}


int main()
{
    vector<Customer> customers;
    Customer c1 = { 150031,"Mr","John",5,333,362,341,"New" };
    customers.push_back(c1);
   
    //InputFileStream();
    

   /* Customer customer;

    customer.setCustomerID(150032);
    customer.setTitle("Mr");
    customer.setName("Joey");
    customer.setNumOfPurchases(3);
    customer.setPurchases(366,352,334);
    customer.setType("New");

    cout << customer.getCustomerID() << endl;
    cout << customer.getTitle() << endl;
    cout << customer.getName() << endl;
    cout << customer.getNumOfPurchases() << endl;
    cout << customer.getPurchases() << endl;
    cout << customer.getType() << endl;
   */
  
    return 0;
   
     
}

Customer.cpp中的代码

#include "Customer.h"
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include<utility>
using namespace std;

//default constructor
Customer::Customer() {
}


//Full constructor
Customer::Customer(int customerID,string title,string name,int numOfPurchases,int purchase1,int purchase2,int purchase3,string type)
{
    this->customerID = customerID;
    this->title = title;
    this->name = name;
    this->numOfPurchases = numOfPurchases;
    purchases = new int[3];
    purchases[0] = purchase1;
    purchases[1] = purchase2;
    purchases[2] = purchase3;
    this->type = type;
}

Customer::Customer(const Customer& source) //copy constructor
{
    cout << "copy constructor called" << endl;
    this->customerID = source.customerID;
    this->title = source.title;
    this->name = source.name;
    this->numOfPurchases = source.numOfPurchases;
    this->purchases = new int[3];
    purchases[0] = source.purchases[0];
    purchases[1] = source.purchases[1];
    purchases[2] = source.purchases[2];
    this->type = source.type;
}

//overloaded assignment operator=
Customer& Customer::operator= (Customer& otherCustomer)
{
    cout << "Overloaded assignment operator= called" << endl;

    //self-assignment guard
    if (this == &otherCustomer)
        return *this;  //refernce to the same object

   // copy data from the source (rhs) to this object (the destination)
    name = otherCustomer.name;

    //must make a new scores object to store a copy of the other student 
    if (purchases != nullptr)
        delete[] purchases;

    purchases = new int[3];
    for (int i = 0; i < 3; i++) {
        purchases[i] = otherCustomer.purchases[i];
    }

    //return this existing object so we can chain this operator 
    return *this;
}

string Customer::save()
{
    stringstream out;
    out << this->customerID << ";";
    out << this->title << ";";
    out << this->name << ";";
    out << this->type << ";\n";
    out << this->numOfPurchases  << ";";
    int* purchases = 0;
    
    out.flush();
    return out.str();
}

void Customer::parse(string line)
{
    stringstream in(line);
    string customerIDLine;
    getline(in,customerIDLine,';');
    customerID = stoi(customerIDLine);
    getline(in,';');
    getline(in,';');

    string numOfPurchases;
    getline(in,numOfPurchases,';');
    int s = stoi(numOfPurchases);

    int* purchasesArray = new int[3];
    purchasesArray[0] = (s & (255 << 16)) >> 16;
    purchasesArray[1] = (s & (255 << 8)) >> 8;
    purchasesArray[2] = s & 255;
}  

void Customer::addCustomer(vector<Customer>& customers )
{
    //after read data 
    int customerID;
    cout << "Please Enter Customer ID: " << endl;
    cin >> customerID; 
    string title;
    cout << "Please Enter Title: " << endl;
    getline(cin,title);
    string name;
    cout << "Please Enter Name: " << endl;
    getline(cin,name);
    string type;
    cout << "Please Enter Type: " << endl;
    getline(cin,type);
    int numOfPurchases;
    cout << "Please Enter Number of Purchases: " << endl;
    cin >> numOfPurchases;
    int purchase1;
    cout << "Please Enter First Purchase: " << endl;
    cin >> purchase1;
    int purchase2;
    cout << "Please Enter Second Purchase: " << endl;
    cin >> purchase2;
    int purchase3;
    cout << "Please Enter Third Purchase: " << endl;
    cin >> purchase3;
    Customer c;
    customers.push_back(c);
    //Customer c();
}


Customer::~Customer() {
    cout << "Destructor ~Customer called" << endl;
    delete[] purchases;
}

// Overloaded insertion operator  (Outputs Character object data as an output stream)
// Defined in header file as a  "friend" function,as it is not a member function
//
ostream& operator<<(ostream& out,Customer& customer)
{
    cout << "Customer details ( output by insertion operator<< )" << endl;
    cout << "Customer ID: " << customer.customerID << endl;
    cout << "Title: " << customer.title << endl;
    cout << "Name: " << customer.name << endl;
    cout << "Number of purchases: " << customer.numOfPurchases << endl;
    cout << "Purchases: ";
    for (int i = 0; i < 3; i++)
    {
        if (i > 0) cout << ",";
        cout << customer.purchases[i];
    }
    cout << "Type: " << customer.type << endl;
    
    return out;
}


istream& operator>> (istream& in,Customer& customer)
{
    cout << "Enter Customer details ( using the extraction operator>> )" << endl;
    cout << "Enter Customer ID: " << endl;
    cin >> customer.customerID;
    cout << "Enter Title: " << endl;
    getline(cin,customer.title);
    cout << "Enter Name: " << endl;
    getline(cin,customer.name);
    cout << "Enter Number of Purchases: ";
    cin >> customer.numOfPurchases; 
    cout << "Enter Purchases: ";
    cin >> customer.purchases[0];
    cin >> customer.purchases[1];
    cin >> customer.purchases[2];
    cout << "Enter Type";
    getline(cin,customer.type);
    cout << endl;

    return in;
}

int Customer::getCustomerID()
{
    return customerID;
}

string Customer::getTitle()
{
    return title;
}

string Customer::getName()
{
    return name;
}

int Customer::getNumOfPurchases()
{
    return numOfPurchases;
}

int* Customer::getPurchases()
{
    return purchases;
}

string Customer::getType()
{
    return type;
}

void Customer::setCustomerID(int customerID)
{
    if (customerID < 1) {
        cout << "Customer ID has to be equal to 1 or more" << endl; //Changed all the "throw invalid_argument" messages to cout as they were causing an issue with my main.cpp file and an abort message kept appearing every time I ran my main.cpp file.
    }
    this->customerID = customerID;
}

void Customer::setTitle(string title)
{
    if (title.length() < 2) {
        cout << "Title has to be more than or equal to 2 characters" << endl;
    }
    this->title = title;
}

void Customer::setName(string name)
{
    if (name.length() < 4) {
        cout << "Length of name should be more than or equal to 4 characters" << endl;
    }
    this->name = name;
}

//Got help ith this on stack overflow as I was using "&&" instead of using "||" for the if statement
void Customer::setNumOfPurchases(int numOfPurchases)
{
    if(numOfPurchases <0 || numOfPurchases > 10000){
        cout << "Number of purchases should be between 0 to 10000" << endl;
    }
    this->numOfPurchases = numOfPurchases;
}

void Customer::setPurchases(int purchase1,int purchase3)
{
    if (purchase1 < 0 || purchase2 < 0 || purchase3 < 0) {
        cout << "Purchases must be more than or equal to zero" << endl;
    }
}

//Got help from stack overflow on comparing strings as I originally didnt use "type.compare"
void Customer::setType(string type) {
    if (type.compare("New") !=0 || type.compare("Either") !=0) {
        cout << "Type of purchase has to be New or Either" << endl;
    }
}

Customer.h中的代码

#pragma once
#include<iostream>
using namespace std;
#include<string>
#include <vector>

class Customer
{
private:
    int customerID;
    string title;
    string name;
    int numOfPurchases;
    int* purchases;
    string type;

public:
    Customer(); // default constructor
    Customer(int customerID,string type);
    
    //copy overload assignment
    Customer& operator=(Customer& otherCustomer);
    Customer(const Customer& source);

    string save();
    void parse(string line);
    void addCustomer(vector<Customer>& customers);
    ~Customer(); //destructor


    //Getters and Setters
    void setCustomerID(int customerID);
    void setTitle(string title);
    void setName(string name);
    void setNumOfPurchases(int numOfPurchases);
    void setPurchases(int purchase1,int purchase3);
    void setType(string type);

    int getCustomerID();
    string getTitle();
    string getName();
    int getNumOfPurchases();
    int* getPurchases();
    string getType();

    void printCustomer() {
        cout << customerID << "," << title << "," << name << "," << numOfPurchases << "," << purchases << "," << type << endl;
    }

    

    friend std::ostream& operator<<(std::ostream& out,Customer& customer); // overloaded operator<<
    friend istream& operator>> (istream& in,Customer& customer); // overloaded operator >>
};

解决方法

向量保存对象。如果矢量之外有对象,则无法在矢量内包含同一对象。您可以在向量中从一个实例移动到另一个实例(这将使实例内部移动,但仍然有两个实例)。 push_back确实会在其参数无法移出时对其参数进行复制。

由于您有一个采用所有必要参数的构造函数,因此可以使用emplace_back来避免复制并在向量中就位构造实例:

customers.emplace_back( customerID,title,name,numOfPurchases,purchase1,purchase2,purchase3,type);
,

std::vector<Customer>按值保存元素,因此仅使用std::vector::push_back会将您的本地对象复制到向量中。

为减轻这种情况,您可以实现移动语义并像这样移动Customer对象:

    #include <utility>
    ....
    Customer c;
    c.parse(line);
    customers.push_back(std::move(c));

或就地构建对象:

    // Post c++17 you could use `push_back()` here as well as 
    // c++17 has mandatory copy elision.
    customers.emplace_back(Customer{}); // or customers.emplace_back(); only
    customers.back().parse(line);

两个解决方案都需要c ++ 11。

您还可以存储引用/指针并在堆上分配对象。

    std::vector<std::unique_ptr<Customer>> customerVector;
    customer_vector.push_back(std::make_unique<Customer>());
    // or customer_vector.push_back(std::make_shared<Customer>());

    std::vector<Customer*> customerVector;
    // Beware for memory leaks,needs to be deleted.
    // Basically never use code like this post c++11).
    customer_vector.push_back(new Customer{});

,

如果您选中std::vector<T,Allocator>::push_back上的引用,则可以通过复制(选项1)或移动(选项2)将其添加到矢量中。

因此,您可以为客户类别创建move constructor

class_name ( class_name && );

然后在向量上使用该移动构造函数(选项2)调用push_back

void push_back( T&& value );