2010-05-24 3 views
1

Моя задача: Создать класс Person with char * name и int age. Реализовать contructor, используя динамическое распределение памяти для переменных, деструктор, функцию init и функцию функции show. Затем преобразуйте этот класс в файл заголовка и cpp и реализуйте в другой программе. Ok так вот мой класс Person:Cpp некоторые основные проблемы

#include <iostream> 
using namespace std; 

class Person { 
    char* name; 
    int age; 
public: 

    Person(){ 
     int size=0; 
     cout << "Give length of char*" << endl; 
     cin >> size; 
     name = new char[size];  
     age = 0; 
    } 

    Person::~Person(){ 
     cout << "Destroying resources" << endl; 
     delete [] name; 
     delete take_age(); 
    } 

    friend void show(Person &p); 

    int* take_age(){ 
     return &age; 
    } 

    char* take_name(){ 
     return name;  
    } 

    void init(char* n, int a) { 
     name = n; 
     age = a; 
    } 
}; 

void show(Person *p){ 
    cout << "Name: " << p->take_name() << "," << "age: " << p->take_age() << endl; 
} 

int main(void) { 
    Person *p = new Person; 
    p->init("Mary", 25); 

    show(p); 

    system("PAUSE"); 
    return 0; 
} 

И теперь с частью заголовка/реализации:
- мне нужно ввести конструктор в файлах заголовка/реализации? Если да - как?
- моя функция show() - это дружественная функция. Должен ли я как-то это учитывать?

Я уже не смог вернуть эту задачу на экзамен, но все же хотел бы знать, как ее реализовать.

+3

Если у вас его еще нет, вам следует рассмотреть возможность получения одной из книг C++ для начинающих, перечисленных в [The Definitive C++ Book Guide and List] (http://stackoverflow.com/questions/388242/the -definitive-с-книга-гид-и-лист). –

+1

Извините, если это звучит суровым, но ваш код действительно показывает, что вы совершенно не знаете, что происходит. Это просто факт. Вам действительно нужно что-то прочитать. –

+0

C++ не называется cpp. Препроцессор C называется cpp. – hobbs

ответ

5

Решите многие проблемы, перейдя с char * на std::string. Вы будете рады, что вы это сделали.

Класс std::string заботится о распределении памяти и освобождении, а также о копировании.

Если это домашняя работа, убедите своего профессора использовать std::string для начинающих и сэкономьте char * для раздела по указателям. Также напомните вашему профессору, что язык C++ отличается от языка C. Это одна из тех областей.

+0

Не согласен. Любой программист на C++ должен знать, как использовать строки char *, а также экземпляры std :: string. C++ - это гораздо более сложный язык, чем C, но оба имеют одни и те же основы, и любой программист на C++ должен их знать. – ebasconp

+0

@ebasconp: должны ли они знать, что это другой вопрос, подходит ли это для этой домашней проблемы. Поэтому я соглашаюсь с Томасом. –

+0

+1 не нужно заставлять людей использовать символ 'char *' в коде C++, за исключением случаев взаимодействия с API-интерфейсами, которые этого требуют. Это просто усложняет ситуацию и оставляет место для утечек памяти/бесхозяйственности. –

4

При использовании delete или delete[] вам не нужен *. Просто поставьте для него переменную указателя, например.

delete[] name; 

Кроме того, ваш take_age член утверждает, что вернуть int*, но вы на самом деле вернуть себе int элемент. Вам нужно взять адрес члена, используя &, если вы хотите это сделать. Как @Jerry прокомментировал это не что вы хотите сделать здесь.

+2

... или изменить его тип возврата на int. Возврат указателя к внутренним объектам - это то, чего вы обычно избегаете (и в этом случае, по-видимому, нет причин для этого). –

+0

@ Джерри: Абсолютно. – Troubadour

2

Я думаю, вы должны исследовать следующие фрагменты кода (например, что под ними, что здесь происходит, и т.д ...)

int * take_age(); // You should return plain `int` here, I assume 


~Person(){ 
    cout << "Destroying resources" << endl; 
    delete *[] name; // Do you understand why did you put `*` here? 
    delete * take_age(); // Do you understand why did you write this? What behaviour you were trying to achieve? 

и, собственно, так далее. Я думаю, что когда вы закончите работу с файлом basic, вы можете перейти к вопросам проектирования заголовков и функций друзей.

3

Хотя некоторые на этом сайте, по-видимому, считают, что это вполне приемлемо, хорошая практика (см. Can a constructor return a NULL value?), вы действительно должны воздерживаться от выполнения таких операций, как потоковые операции внутри конструктора вашего объекта. Сделайте этот поток, читающий снаружи, а затем вызовите функцию с результатами.

То есть, ИМХО, первый шаг, который вы должны предпринять.

+0

При всем уважении, это в лучшем случае не имеет значения, в худшем случае, совершенно неправильно. Проблемы OP исходят из знания новичком языка, и вам не помогает ваш совет по доктрине. Совершенно прекрасно иметь конструктор, который десериализуется, даже если вы лично им не нравитесь. Фактически, для неизменяемых объектов это практически единственный способ реализовать сериализацию. –

+0

Да, я думаю, это только хороший совет, если они хотят выйти и получить работу в поле. Хотя это также хороший совет, если они хотят быть достойным разработчиком хобби. Если все, что они хотят быть, это взлом, хотя, если вызовы на консольные потоки в конструкторе выполняются ... –

+0

Нет, это нехороший совет для всех. Я не видел никаких обоснованных аргументов в поддержку вашего вывода о том, что конструкторы должны сводить к минимуму их действия и много хороших аргументов об обратном. Ограничение себя произвольно из-за какой-то религиозной веры не является альтернативой хакеру, это альтернатива тому, чтобы быть компетентным профессионалом. –

0

Я думаю, что ваша проблема связана с этой строкой: friend void (Person & p); Для чего это необходимо.

Мне нужно ввести конструктор в файлы заголовка/реализации? Конструктор может быть в файле .h или .cpp. Это не имеет значения. Как правило, если функция короткая, ее можно включить в файл .h. Что-нибудь дольше должно идти в .cpp.

Функция my show() - это дружественная функция. Не уверен, что вы подразумеваете под этим. функции друга существуют вне определения класса. Ваша функция show определена внутри класса, поэтому не нужно быть другом.

3

В типичном случае управление указателем и блоком динамически распределенной памяти (например, имя в этом случае) является достаточной ответственностью для одного класса. Таким образом, Томас Мэтьюз прав: вы должны действительно использовать строку в этом случае. Если вы собираетесь справиться с этим самостоятельно, вы все равно должны отделить эту ответственность от своего собственного класса и вставить объект этого класса в свой объект Person. Во всяком случае, std::string уже пытается сделать слишком много; вам будет лучше с чем-то, что делает меньше, а не больше.

Ваши удаления должны совпадать с вашими распределениями. В этом случае единственное распределение является:

name = new char[size];  

поэтому только удаление должно быть:

delete [] name; 

Насколько friend функции идти, вы обычно хотите друга декларации внутри определения класса, но определение функции вне определения класса:

class Person { 
// ... 
    friend void show(Person const &); 
// ... 
}; 

void show(Person const &p) { 
    // ... 
} 

Есть и другие возможности, но это общая идея. В частности, друг никогда не является членом. То, что у вас было, было объявлением одной (глобальной) функции с именем show и определением полностью отдельной функции-члена, которая имела одно и то же имя, но на самом деле была не совсем той же самой функцией.

Это показывает еще один момент: const-correctness. Вы передавали параметр в качестве ссылки на Person. Если он не изменит объект Person (в этом случае show() выглядит как плохой выбор имени), он, вероятно, должен ссылаться на объект const. Же общая идея относится к take_age() - поскольку она только возвращает значение, то оно должно быть const функцией:

int take_age() const { return age; } 

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

1

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

Из вашего описания наверху, я думаю, вы, возможно, неправильно поняли некоторые из того, что было предложено для этого задания. Во-первых, моя интерпретация будет заключаться в том, что установка значения имени и возраста должна выполняться в функции init(), а не в конструкторе. Как упоминалось несколькими другими плакатами, ваш конструктор должен просто инициализировать ваш класс в хорошо известном состоянии. Например,

Person() { 
    name = NULL; 
    age = 0; 
} 

Затем в вашей функции инициализации вы можете назначить значения. Если вы посмотрите на свою исходную функцию init(), следует упомянуть, что просто присвоение значения указателя (char *) другому указателю (char *) копирует только значение указателя, а не данные, которые он представляет. Таким образом, для присвоения значения имени вам нужно рассчитать размер необходимого вам буфера, выделить буфер и скопировать данные самостоятельно.Функция основной инициализации(), вероятно, выглядеть

init(const char *n, int a) { 
    // Calculate the required name length plus a NULL-terminator 
    size_t nameLen = strlen(n) + 1; 

    // Free any previous value. 
    if (name != NULL) { 
     delete[] name; 
    } 

    // Allocate the name buffer and copy the name into it. 
    name = new char[nameLen]; 
    memcpy(name, n, nameLen); 

    // Store the age. 
    age = a; 
} 

Наконец, в вашем деструкторе вы освободить любые ресурсы, выделяемые вашим классом, в данном случае буфер имя.

~Person() { 
    if (name != NULL) { 
     delete[] name; 
    } 
} 

Если у вас есть книга или что-то связанное с вашим классом, вы можете просмотреть информацию о указателях. Они могут быть немного сложными, но важны для изучения. Я подозреваю, поэтому проблема задана с помощью char * для строк, а не для строкового класса STL.

На ваш вопрос о размещении информации в заголовках и исходных файлах часто считается хорошей практикой создание файла заголовка, который содержит декларации класса и прототипы функций-членов, а затем предоставляет реализацию ваших методов в отдельном исходном файле. Для некоторых простых функций вы можете обеспечить реализацию непосредственно в своем файле заголовка.

Ключ при предоставлении определений членов класса в отдельном исходном файле должен предоставить имя класса для правильной области действия функции (то есть Person: :). Так что ваш файл заголовка может содержать определение класса, как

// Header file (e.g., person.h) 

class Person { 
private: 
    char *name; 
    int age; 

public: 
    Person() { name = NULL; age = 0 }; 
    ~Person() { if (name != NULL) delete[] name; } 

    void init(const char *n, int a); 

    // Other method declarations and/or definitions 
}; 

И затем в исходном файле

// Source file (e.g., person.cpp) 

void Person::init(const char *n, int a) { 
    // ... 
} 

// Rest of method definitions 

исходные файлы, которые используют свой человек класс только нужно включить заголовочный файл с вашим определением класса.

0

в дополнении к ранее опубликованным ответам, я получил два очка советов для вас:

  • не использовать «друг». некоторые здесь могут не согласиться со мной, но «друг» на самом деле не должен быть частью C++, поскольку он противоречит тому, что означает ООП.
  • Назовите свои методы: не назовите свои методы, как «take_name» или «take_age». традиционно, поскольку они являются геттерами, рассмотрите их имена «getName» и «getAge». вы в конечном итоге с большим уважением от разработчиков таким образом.
+0

Хотя «друг», безусловно, может быть запахом кода, и есть часто лучшие способы, есть также места, где это очень необходимо, в том числе в STL. Это может быть не чисто ООП, но и не C++. Однако согласился с именами. –

Смежные вопросы