2014-11-21 2 views
-3

Я недавно изучал C++, исходя из фона в Java и других языках высокого уровня. У меня возникли проблемы с указателями и объектами, и мне еще предстоит найти хороший ресурс, который объясняет, как манипулировать объектами, созданными с помощью пользовательских классов, с помощью указателей.Взаимодействие между указателями и классами

Я написал простую программу, чтобы попытаться сузить, где моя проблема.

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

#include <iostream> 
#include <cstdlib> 
#include <string.h> 

using namespace std; 

class Point { 
public: 
    float x; 
    float y;  
public: 
    Point(float x, float y) { 
     this->x=x; 
     this->y=y; 
    } 
    Point() { 
     x=0.0f; 
     y=0.0f; 
    } 
    ~Point() { 
     x=0.0f; 
     y=0.0f; 
    } 
}; 

class Place { 
public: 
    string name; 
    Point location; 
public: 
    //Constructor with arguements. 
    Place(string nam, Point loc) { 
     name = nam; 
     location = loc; 
    } 
    //Default constructor. 
    Place() { 
     name = "Default"; 
     location = {0.0f,0.0f}; 
    } 
    //Destructor. This object contains no pointers so nothing to delete. 
    ~Place() { 

    } 
public: 
    void setName(string nam) { 
     name = nam; 
    } 
    void setLocation(Point loc) { 
     location = loc; 
    } 
}; 

class PointerToAPlace { 
public: 
    Place *place; 
public: 
    PointerToAPlace(Place *pl) { 
     *place = *pl; 
    } 
    ~PointerToAPlace() { 
     delete place; 
    } 
}; 

int main(int argc, char** argv) { 
    std::cout << "\n"; 
    std::cout << "Make some places and hope they don't segfault.\n"; 


    /*Object created with default constructor. Will be automatically deleted at the end of this scope.*/ 
    Place placeA; 
    std::cout << "Place named " << placeA.name.c_str() << " is at " << placeA.location.x << "," << placeA.location.y << " and is lovely.\n"; 
    //Output: Place named Default is at 0,0 and is lovely. 


    /*Object created with bracket constructor. Will be automatically deleted at the end of this scope.*/ 
    Place placeB = {"Lordran", Point{12.2f,99.3f}}; 
    std::cout << "Place named " << placeB.name.c_str() << " is at " << placeB.location.x << "," << placeB.location.y << " and is lovely.\n"; 
    //Output: Place named Lordran is at 12.2,99.3 and is lovely. 


    /*Object created with new keyword. This is a pointer and must be explicitly deleted.*/ 
    Place *placeC = new Place("Drangleic", Point{-123.34f,69.69f}); 
    std::cout << "Place named " << placeC->name.c_str() << " is at " << placeC->location.x << "," << placeC->location.y << " and is lovely.\n"; 
    delete placeC; 
    //Output: Place named Drangleic is at -123.34,69.69 and is lovely. 


    /*Array of objects, created with default constructor and not assigned any values. Will be automatically deleted at the end of this scope.*/ 
    Place placeD[3]; 
    std::cout << "Here are some places named: "; 
    for(int i=0;i<3;i++) { 
     std::cout << placeD[i].name.c_str(); 
     std::cout << (i<2 ? ", " : ". "); 
    } 
    std::cout << "They are quite the places!\n"; 
    //Output: Here are some places named: Default, Default, Default. They are quite the places!  


    /*Array of objects, created with default constructor and then assigned values. Will be automatically deleted at the end of this scope.*/ 
    Place placeE[5]; 
    string namesA[5] = {"Boletaria", "Stonefang", "Latria", "Shrine of Storms", "Valley of Defilement"}; 
    for(int i=0;i<5;i++) { 
     placeE[i].setName(namesA[i]); 
     placeE[i].setLocation(Point{1.23f, 3.21f}); 
    } 
    std::cout << "Here are some places named: "; 
    for(int i=0;i<5;i++) { 
     std::cout << placeE[i].name.c_str(); 
     std::cout << (i<4 ? ", " : ". "); 
    } 
    std::cout << "They are quite the places!\n"; 
    //Output: Here are some places named: Boletaria, Stonefang, Latria, Shrine of Storms, Valley of Defilement. They are quite the places! 


    /*Trying to do a pointer to an array of objects.*/ 
    Place *placeF[7]; 
    string namesB[7] = {"Astora", "Carim", "Thoroughland", "Catarina", "Baulder", "Vinheim", "Zena"}; 
    for(int i=0;i<7;i++) { 
     placeF[i]->setName(namesB[i]); 
     placeF[i]->setLocation(Point{2.23f, 6.21f}); 
    } 
    std::cout << "Here are some places named: "; 
    for(int i=0;i<7;i++) { 
     std::cout << placeF[i]->name.c_str(); 
     std::cout << (i<6 ? ", " : ". "); 
    } 
    std::cout << "They are quite the places!\n"; 
    //Output: SEGFAULT D: 

    /*Trying to put a pointer to a place into an object, then delete it.*/ 
    Place *placeG = new Place("Anor Londo", Point{-12312.34f,33.69f}); 
    PointerToAPlace pnt = {placeG}; 
    std::cout << "Place named " << pnt.place->name.c_str() << " is at " << pnt.place->location.x << "," << pnt.place->location.y << " and is lovely.\n"; 
    //Output: SEGFAULT :(

    return 0; 
} 
+0

Пожалуйста, задайте конкретный вопрос. –

+0

Что вы думаете? –

+0

Прежде всего, это даже не нужно компилировать. 'namesB' имеет слишком много строк в инициализаторе. Во всяком случае, вы используете неинициализированные указатели, как если бы они указывали на действительный объект.Я также хотел бы рассмотреть возможность публикации на http://codereview.stackexchange.com советов о том, как лучше использовать язык (например, бесполезный деструктор, который у вас есть для «Point»). – chris

ответ

6
PointerToAPlace(Place *pl) { 
    *place = *pl; 
} 

Это неправильно, если то, что вы пытаетесь сделать, это сохранить указатель затем сделать что:

PointerToAPlace(Place* pl) { 
    place = pl; 
} 

Или еще лучше, используйте список конструктор инициализатора:

PointerToAPlace(Place* pl) : place(p1) { 
} 

Ваш текущий код не устанавливает place, поэтому он содержит значение нежелательной почты, указывающее на то, кто знает, где, а затем разыгрывает его (что является неопределенным поведением).

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

IMHO после того, как вы узнали, как работает синтаксис и как использовать new/delete, вы должны прекратить использовать их и вместо этого использовать интеллектуальные указатели.

+0

Спасибо. Это намного больше смысла, и все сейчас компилируется/выполняется. Я также рассмотрю, как копирование работает в C++ в отношении указателей. В других языках я всегда определяю конкретный метод copy() для классов, который не позволяет обмениваться ссылками с оригиналом. Я предполагаю, что я должен сделать то же самое в C++. –

+2

Для этого нужен конструктор копирования. –

3
class PointerToAPlace { 
public: 
    Place *place; 
public: 
    PointerToAPlace(Place *pl) { 
     *place = *pl; 
    } 
    ~PointerToAPlace() { 
     delete place; 
    } 
}; 

Это неправильно для начала. Вы откладываете place, прежде чем делать это в любом месте.

Предположительно, вы имели в виду place = pl; * является частью типа, а не названия.

Это будет более понятно вам, если вы напишете объявления указателей как Place* pl, а не Place *pl; не слушайте скептиков с 1970-х годов, которые зацикливаются на крайнем случае объявлений с несколькими переменными, так как это только недостатком этих обозначений.

+0

Объявления с несколькими переменными похожи на: int i, j, k; или int i = 0, j = 0, k = 0; правильно? –

+1

@newObjekt: Правильно. –

2
/*Trying to do a pointer to an array of objects.*/ 
Place *placeF[7]; 

Нет, это массив из семи (неинициализированными) указателей на Place. Поэтому, когда вы разыгрываете их, вы получаете segfault.

+0

Спасибо, я этого не знал. Получил это сейчас. :) –

0

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

Point(Point & original) 
{ 
    // initialize this with original 
} 

EDIT: Добавить отсутствующий «&» в фрагменте коды.

+0

Хорошая точка в целом, но это не допустимый конструктор копирования, он должен взять свой параметр по ссылке. В противном случае вызов будет пытаться скопировать объект для инициализации аргумента, который вызывает конструктор копирования, который пытается скопировать аргумент, который вызывает конструктор копирования, который ... –

+1

Точка не требует конструктора копирования. –