Вектор указателей объектов, общая помощь и путаница - PullRequest
1 голос
/ 28 июня 2011

У меня есть домашнее задание, в котором я должен создать вектор указателей на объекты

Позже во время загрузки я буду использовать наследование / полиморфизм, чтобы расширить класс, включив в него плату за двухдневную доставку, доставку на следующий день и т. Д. Однако это не моя проблема сейчас. Конечная цель текущей программы - просто распечатать содержимое каждого объекта в векторе (имя и адрес) и определить его стоимость доставки (вес * стоимость).

Моя проблема не в логике, я просто запутался в нескольких моментах, связанных с объектами / указателями / векторами в целом. Но сначала мой код. Я в основном вырезал все, что не имеет значения сейчас, в основном, будет иметь пользовательский ввод, но сейчас я жестко запрограммировал два примера.

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

class Package {
public:
    Package(); //default constructor
    Package(string d_name, string d_add, string d_zip, string d_city, string d_state, double c, double w);
    double calculateCost(double, double);
    ~Package();

private:    
    string dest_name;
    string dest_address;
    string dest_zip;
    string dest_city;
    string dest_state;
    double weight;
    double cost;

};

Package::Package()
{
    cout<<"Constucting Package Object with default values: "<<endl;
    string dest_name="";
    string dest_address="";
    string dest_zip="";
    string dest_city="";
    string dest_state="";
    double weight=0;
    double cost=0;
}
Package::Package(string d_name, string d_add, string d_zip, string d_city, string d_state, string r_name, string r_add, string r_zip, string r_city, string r_state, double w, double c){

    cout<<"Constucting Package Object with user defined values: "<<endl;
    string dest_name=d_name;
    string dest_address=d_add;
    string dest_zip=d_zip;
    string dest_city=d_city;
    string dest_state=d_state;
    double weight=w;
    double cost=c;
}
Package::~Package()
{
    cout<<"Deconstructing Package Object!"<<endl;
    delete Package;
}
double Package::calculateCost(double x, double y){
    return x+y;
}
int main(){
    double cost=0;
    vector<Package*> shipment;
    cout<<"Enter Shipping Cost: "<<endl;
    cin>>cost;
    shipment.push_back(new Package("tom r","123 thunder road", "90210", "Red Bank", "NJ", cost, 10.5));
    shipment.push_back(new Package ("Harry Potter","10 Madison Avenue", "55555", "New York", "NY", cost, 32.3));
    return 0;

}

Итак, мои вопросы:

  1. Мне сказали, что я должен использовать вектор указателей объектов, а не объектов. Зачем? Мое назначение требует этого конкретно, но мне тоже сказали иначе работать не будет.
  2. Где я должен создавать это вектор? Должно ли это быть частью моего пакета Учебный класс? Как мне добавить объекты в это тогда?
  3. Нужен ли мне конструктор копирования? Почему?

  4. Как правильно деконструировать мой вектор указателей объектов?

Любая помощь будет принята с благодарностью. Я искал много связанных статей здесь, и я понимаю, что у моей программы будут утечки памяти. Использование одного из специализированных ptrs из boost :: не будет доступно для использования. Прямо сейчас я больше обеспокоен созданием основы моей программы. Таким образом, я действительно могу перейти к функциональности, которую мне нужно создать.

Спасибо.

Ответы [ 3 ]

6 голосов
/ 28 июня 2011

Вектор указателей можно повторно использовать для хранения объектов подклассов:

class Person
{
    public:
    virtual const std::string& to_string () = 0;
    virtual ~Person () { } 
};

class Student : public Person
{
   const std::string& to_string ()
   {
       // return name + grade
   }
};

class Employee : public Person
{
   const std::string& to_string ()
   {
      // return name + salary
   }
};

std::vector<Pointer*> persons;
person.push_back (new Student (name, grade));
person.push_back (new Employee (name, salary));
person[0]->to_string (); // name + grade
person[1]->to_string (); // name + salary

В идеале вектор должен быть заключен в класс. Это облегчает управление памятью. Это также облегчает изменение структуры данных поддержки (здесь std::vector) без нарушения существующего клиентского кода:

class PersonList
{
   public:
   Person* AddStudent (const std::string& name, int grade)
   {
       Person* p = new Student (name, grade);
       persons.push_back (p);
       return p;
   }

   Person* AddEmployee (const std::string& name, double salary)
   {
       Person* p = new Employee (name, salary);
       persons.push_back (p);
       return p;
   }

   ~PersonList ()
   {
      size_t sz = persons.size ();
      for (size_t i = 0; i < sz; ++i)
          delete persons[i];
   }

   private
   std::vector<Person*> persons;
};

Таким образом, мы можем переписать наш код как:

{
   PersonList persons;
   Person* student = persons.AddStudent (name, grade);
   Person* employee = persons.AddEmployee (name, salary);
   student.to_string ();
   employee.to_string ();
} // The memory allocated for the Person objects will be deleted when
  // `persons` go out of scope here.

Знакомство с Правило трех поможет вам решить, когда добавлять конструктор копирования в класс. Также прочитайте о const правильности .

2 голосов
/ 28 июня 2011

Вопрос 1: Вы упомянули наследство.Поскольку унаследованные объекты часто требуют больше байтов памяти, они не помещаются на место базового объекта.Если вы попытаетесь вставить их, вместо этого вы получите базовый объект.Это называется нарезкой объектов.

Вопрос 2. Прежде чем писать код, спроектируйте его.Есть куча возможных решений.Для начала вы можете оставить его в main (), но позже вы будете вынуждены создать класс, подобный PackageContainer для хранения ваших объектов.

Вопрос 3 + 4: вам нужен конструктор копирования, назначениеoperator = и деструктор, когда объект класса владеет динамически размещаемыми объектами (Правило Большой Тройки).Так что PackageContainer, вероятно, понадобится им.Вы создаете объекты динамически, используя new Object(..).Вы несете ответственность за их уничтожение и возвращение их памяти в систему непосредственно перед уничтожением вектора указателей:

for (size_t i = 0; i < shipment.size(); ++i)
{
  delete shipment[i];
}

Поскольку работа с открытыми указателями для динамически размещаемых объектов небезопасна, рассмотрите возможность использования

std::vector<tr1::shared_ptr<Package> > shipment;

или

std::vector<std::shared_ptr<Package> > shipment;

, если ваш компилятор понимает C ++ 0x.shared_ptr позволяет вам освободить память: он реализует правило большой тройки для одного указателя объекта.Он должен использоваться в коде качества продукции.

Но попробуйте сделать это правильно и с помощью голых указателей.Я думаю, что это то, о чем твое домашнее задание.

1 голос
/ 28 июня 2011

Мне сказали, что я должен использовать вектор указателей объектов, а не объектов. Зачем? Мое назначение требует специально, но мне также сказали, что иначе это не сработает.

Обычно следует избегать использования вектора объектов, чтобы избежать проблемы Срезы объектов . Чтобы заставить полиморфизм работать, вы должны использовать какие-то указатели. Я не уверен в том, как выровнены классы в вашем назначении, но, вероятно, у вас где-то там может быть Inheritance, и, следовательно, если vector хранит объекты базового класса и вы вставляете в него объекты класса Derived, то это приведет к срезанию членов производного класса выкл.

Лучшим решением будет использование интеллектуального указателя вместо необработанного указателя. STL имеет auto_ptr, но его нельзя использовать в стандартном контейнере. Умные указатели будут лучшим решением, но, как вы уже сказали, вы не можете использовать Boost. Так что в вашем случае вы можете использовать реализацию интеллектуальных указателей вашего компилятора. , который входит в TR1 пространство имен, помните, однако, что есть некоторые разногласия в пространстве имен для функций TR1 (Visual C ++ помещает их в std::, в то время как GCC помещает их в std::tr1::).

Где мне создавать этот вектор? Должен ли он быть частью моего пакета класса? Как мне тогда добавить в него объекты?
В вашем примере кода уже есть пример добавления указателя на класс Package в векторе. В двух словах, вы будете динамически размещать указатели на Package и затем добавлять их к вектору.

Нужен ли мне конструктор копирования? Почему?
Конструктор копирования, сгенерированный компилятором, выполняет копирование по элементам. Иногда этого недостаточно. Например:

class MyClass {
    public:
        MyClass( const char* str );
        ~MyClass();
    private:
        char* str;
    };

    MyClass::MyClass( const char* str2 )
    {
        str = new char[srtlen( str2 ) + 1 ];
        strcpy( str, str2 );
    }

    Class::~Class()
    {
        delete[] str;
    }

В этом случае копирование элемента str по элементам не будет дублировать буфер (будет скопирован только указатель (shallow copy)), поэтому первая уничтоженная копия, разделяющая буфер, успешно вызовет delete[] а второй столкнется с Undefined Behavior. Вам нужно deep copying copy constructor (и оператор присваивания также) в таком сценарии.

Когда лучше использовать пользовательский конструктор копирования, лучше всего определить Правило трех :

Whenever you are writing either one of Destructor, Copy Constructor or Copy Assignment Operator, you probably need to write the other two.

Как правильно деконструировать мой вектор указателей объектов?
Вам придется явно вызывать delete для каждого содержащегося в нем указателя, чтобы удалить содержимое, на которое он указывает.

vector::erase
Удаляет из векторного контейнера и вызывает его деструктор, но если содержащийся объект является указателем, он не берет на себя ответственность за его уничтожение.

Проверьте этот ответ здесь, чтобы узнать, как правильно удалить вектор указателя на объекты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...