Проблема с созданием класса

Проблема с созданием класса

Всем привет. Решаю задания на cppstudio.com. Дошёл да задания: http://cppstudio.com/post/8318/
Пишу код:
Файл main.cpp:

#include <iostream>

using namespace std;

#include "func&class.h"

int main()
{
    setlocale ( 0, "" );
    zooshop animal ( "Cat", "Petya", men, 57, 2 );
    cout << animal;
    return 0;
}

Ну и файл func&class.h, соответственно:

const int SIZE = 256;

const bool men = true;
const bool women = false;

bool _strcmp ( const char *first, const char *second );
void _strcpy ( const char *out, char *in );
int _strlen ( const char *str );

struct bit_status
{
    unsigned kind :2;
    unsigned name :2; 
    unsigned sex :2;
    unsigned price :2;
    unsigned number :2;
};

class zooshop
{
    private:
        char _kind_animal[SIZE];
        bool _sex;
        char _name[SIZE];
        double _price;
        unsigned int _number;
    public:
        zooshop();
        zooshop( zooshop &ob );
        zooshop( const char *a, const char *n, bool s,  double p, unsigned int nn );        
        char *kind_animal();
        void kind_animal( const char *new_kind );       
        char *name();
        void name( const char *new_name );      
        bool sex() { return _sex; }
        void sex ( bool new_sex ) { _sex = new_sex; }       
        double price() { return _price; }
        void price( double new_price ) { _price = new_price; }      
        unsigned int number() { return _number; }
        void number( unsigned int new_n ) { _number = new_n; }  
        void set_all ( const char *new_kind, const char *new_name, bool s,  double p, unsigned int nn );
        void get_all ( char *get_kind, char *get_name, bool &s,  double &p, unsigned int &n );      
        friend istream &operator>> ( istream stream, zooshop &ob );
        friend ostream &operator<< ( ostream stream, zooshop &ob );
        bit_status operator,(zooshop ob);
        bool operator<(zooshop ob);
        bool operator>(zooshop ob);
        bool operator!=(zooshop ob);
        bool operator==(zooshop ob);
        ~zooshop(); 
};


int _strlen ( const char *str )
{
    int len;
    for ( len = 0; str[len]; len++ );
    return len;
}

bool _strcmp ( const char *first, const char *second )
{
    register int i;
    if ( _strlen(first) != _strlen(second) ) return false;
    for ( i = 0; first[i]; i++ )
        if ( first[i] != second[i] ) return false;
    return true;
}

void _strcpy ( const char *out, char *in )
{
    register int i;
    for ( i = 0; out[i]; i++ )
        in[i] = out[i];
    return;
}

zooshop :: zooshop()
{
    _strcpy ( "Wolf", _kind_animal );
    _strcpy ( "Buba", _name );
    _sex = men;
    _price = 999;
    _number = 15;
}

zooshop :: zooshop ( zooshop &ob )
{
    _strcpy ( ob._kind_animal, _kind_animal );
    _strcpy ( ob._name, _name );
    _sex = ob._sex;
    _price = ob._price;
    _number = ob._number;
}

zooshop :: zooshop ( const char *a, const char *n, bool s, double p, unsigned int nn )
{
    _strcpy ( a, _kind_animal );
    _strcpy ( n, _name );
    _sex = s;
    _price = p;
    _number = nn;
}

char* zooshop :: kind_animal ()
{
    char ret[SIZE];
    _strcpy ( _kind_animal, ret ); 
    return ret;
}

void zooshop :: kind_animal ( const char *new_kind )
{
    _strcpy ( new_kind, _kind_animal );
    return;
}

char* zooshop :: name ()
{
    char ret[SIZE];
    _strcpy ( ret, _name );
    return ret;
}

void zooshop :: name ( const char *new_name )
{
    _strcpy ( new_name, _name );
    return;
}

void zooshop :: set_all ( const char *new_kind, const char *new_name, bool s,  double p, unsigned int n )
{
    _strcpy ( new_kind, _kind_animal );
    _strcpy ( new_name, _name );
    _sex = s;
    _price = p;
    _number = n;
    return;
}

void zooshop :: get_all ( char *get_kind, char *get_name, bool &s, double &p, unsigned int &n )
{
    _strcpy ( _kind_animal, get_kind );
    _strcpy ( _name, get_name );
    s = _sex;
    p = _price;
    n = _number;
    return;
}

istream &operator>> ( istream stream, zooshop &ob )
{
    stream >> ob._kind_animal
           >> ob._name
           >> ob._sex
           >> ob._price
           >> ob._number;
    return stream;
}

ostream &operator<< ( ostream stream, zooshop &ob )
{
    stream << "Вид животного: "
           << ob._kind_animal 
           << endl
           << "Имя: "
           << ob._name
           << endl
           << "Пол животного: ";
    if ( ob._sex ) stream << "m" << endl;
    else stream << "w" << endl;
    stream << "Цена за одну штуку: "
           << ob._price
           << endl
           << "Количество на складе: "
           << ob._number
           << endl;
    return stream;     
}

bit_status zooshop :: operator, ( zooshop ob )
{
    bit_status ret;
    if ( _strcmp( _name, ob._name ) ) ret.name = 2;
    else ret.name = 0;
    if ( _strcmp ( _kind_animal, ob._kind_animal ) ) ret.kind = 2; 
    else ret.kind = 0;
    if ( _sex == ob._sex ) ret.kind = 2;
    else ret.sex = 0;
    if ( _price == ob._price ) ret.kind = 2;
    else
        if ( _price < ob._price ) ret.price = 0;
        else ret.price = 1;
    if ( _number == ob._number ) ret.number = 2;
    else
        if ( _number < ob._number ) ret.number = 0;
        else ret.number = 1;
    return ret;
}

bool zooshop :: operator< ( zooshop ob )
{
    return _number < ob._number;
}

bool zooshop :: operator> ( zooshop ob )
{
    return _number > ob._number;
}

bool zooshop :: operator!= ( zooshop ob )
{
    return _number != ob._number;
}

bool zooshop :: operator== ( zooshop ob )
{
    return _number == ob._number;
}

zooshop :: ~zooshop ()
{
    cout << "Внутри деструктора." << endl;
}

Как видно, в главном файле пока только тест на вывод ин-фы, и он не прошёл.
Выдаёт ошибки:
'std::ios_base::ios_base(const std::ios_base&)' is private, а ругается он на строчку в файле ios_base.h, где объявлено:

private:
    ios_base(const ios_base&);

а так же initializing argument 1 of 'std::ostream& operator<<(std::ostream, zooshop&)' на строчку:

ostream &operator<< ( ostream stream, zooshop &ob )

Мне лично вообще ничего не понятно.Может вам что-то известно по данному поводу?
P.S.:Как вы, наверное, заметили, в функции name и kind_animal
есть такая конструкция:

char* zooshop :: kind_animal ()
{
    char ret[SIZE];
    _strcpy ( _kind_animal, ret ); 
    return ret;
}

она предназначена для того, чтобы случано в вызывающей среде, где получили указатель на строку с именем, не поменяли это самое имя. Имя копируется в отдельную строку, а затем эта строка(точнее указатель на неё) возвращается. Но компилятору мои амбиции не по душе. Он выдаёт предупреждение:
address of local variable 'ret' returned
В чём дело?

Первое:

        friend istream &operator>> ( istream& stream, zooshop &ob );
        friend ostream &operator<< ( ostream& stream, zooshop &ob );

Второе. Компилятор правильно ругается: после выхода из функции, память, отведённая в стеке под массив ret будет освобождена и, скорее всего, её содержимое будет изменено при следующем вызове любой функции или при объявлении локальной переменной.

Выхода из этой ситуации, как минимум, три:

(1) Объявить/определить метод так:

const char* zooshop :: kind_animal () const
{
    return _kind_animal;
}

Что однако не спасает от изменения содержимого строки при помощи явного приведения типа к char *.

(2) Создавать копию строки в куче и возвращать на неё указатель. Это совсем плохо, поскольку память надо будет освобождать (а) явно и (б) совсем в другом месте.

(3) Копировать строку из класса в буфер, предоставляемый вызывающим кодом. Это наиболее приемлемый вариант.

char* zooshop :: kind_animal (char *buf)
{
    _strcpy ( _kind_animal, buf ); 
    return buf;
}

Кстати, непонятно почему ты так переживаешь за содержимое поля _kind_animal. Ты сам предоставляешь открытый метод void kind_animal( const char *new_kind ) для изменения этого поля.

Третье. Всякая мелочь.

(1) Зачем писать код для функций

bool _strcmp ( const char *first, const char *second );
void _strcpy ( const char *out, char *in );
int _strlen ( const char *str );

когда в <string.h> имеются их аналоги? Кроме того, вместо функции strcpy() рекомендуется использовать функцию strncpy() с контролем длины строки.

(2) Функция сравнения строк написана неаккуратно. Если вторая строка короче первой, то возможен выход за границу массива.

bool _strcmp ( const char *first, const char *second )
{
    register int i;
    if ( _strlen(first) != _strlen(second) ) return false;
    for ( i = 0; first[i]; i++ )
        if ( first[i] != second[i] ) return false;  // !!! возможен выход за границу массива
    return true;
}

(3) Аналогично с функцией _strcpy(): отсутствует контроль выхода за границу массива.

(4) Дублирование кода в:

void set_all ( const char *new_kind, const char *new_name, bool s,  double p, unsigned int nn );

zooshop( const char *a, const char *n, bool s,  double p, unsigned int nn );

// и почти
zooshop( zooshop &ob );

лучше использовать вызов метода set_all() в конструкторах. Тогда, при необходимости изменений, править придётся только в одном месте.

(5) Для данного класса деструктор пока не нужен.

(6) Заголовочный файл (.h) должен содержать только объявления. Реализация должна быть в файле .cpp.

(7) При такой перегрузке оператора , нарушается семантика. Так делать не рекомендуется.

(8) Перегрузку операторов типа

bool zooshop :: operator< ( zooshop ob );

лучше реализовывать так:

bool zooshop :: operator< ( const zooshop& ob );

При такой сигнатуре при вызове метода не будет выполняться создание временного объекта типа zooshop для параметра.

(9) Всё?..

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.

Ответить

Вы можете использовать разметку markdown для оформления комментариев и постов. Используйте функцию предпросмотра для проверки корректности разметки.

Пожалуйста, оформляйте исходный код в соответствии с правилами разметки. Для того, чтобы вставить код в комментарий, скопируйте его в текстовое поле ниже, после чего выделите то, что скопировали и нажмите кнопку «код» в панели инструментов. Иначе ваш код может принять нечитаемый вид.

Либо производите оформление кода вручную, следующим образом:

``` #include <iostream> using namespace std; int main() { // ... } ```

Предпросмотр сообщения

Ваше сообщение пусто.