Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > Конструкторы и потоки


Автор: yorgan 10.6.2008, 17:52
Подскажите плиз ссылку на хорошую статью, где подробно описано все по конструкторам (по умолчанию, с параметрами, копирования, способы вызова конструкторов, еще какие-нибудь "тонкие" места) и по потокам.
Если кто-то обладает достаточными теоретическими знаниями и не прочь сам написать мануал, буду только очень благодарен!
Заранее спасибо!

Автор: Rififi 10.6.2008, 17:58
http://www.ozon.ru/context/detail/id/85559/
user posted image

Автор: bsa 10.6.2008, 18:07
yorgan, а что именно у тебя вызывает затруднение?
С конструкторами все очень просто. Они вызываются при создании объекта класса. Бывает конструктор по умолчанию. Это конструктор, у которого не указываются параметры. Есть конструктор копирования - он принимает ссылку на объект того же класса. Все конструкторы с одним параметром (в т.ч. и копирования) имеют интересную особенность, они могут вызываться, если в месте инициализации объекту присваивается что-то, для чего есть конструктор или что может быть неявно преобразовано к тому, для чего есть конструктор. Чтобы это не происходило, существует ключевое слово explicit. Примеры:
Код
class MyClass
{
public:
    MyClass() {} //это конструктор по умолчанию
    MyClass(const MyClass &obj); //это конструктор копирования, он не меняет объект, на который ссылается obj
    MyClass(int x); //это конструктор с одним параметром типа int
    explicit MyClass(const char *str); //это конструктор с одним параметром типа "указатель на строку"
};

...

MyClass c1; //ok - вызовется конструктор по умолчанию
MyClass c2(c1), c3 = c1; //в обоих случаях вызовется конструктор копирования
MyClass c4 = 15; //вызовется конструктор с параметром типа int
MyClass c5 = "asdfg"; //ошибка - нельзя таким образом вызывать explicit конструктор
MyClass C6("asdfg"); //ok - вызовется конструктор с параметром типа const char *

Автор: rrrFer 10.6.2008, 19:12
yorgan, 
если под потоком имеется ввиду процесс то вот - присоединяю файл(там очень вкратце написано)
если же под потоками имеются ввиду вообще потоки, то можно прочитать в книжке Павловской "программироание на языке высокого уровня"  -гугли. Там написано крайне просто и понятно.
Вообще поток можно видеть как класс с определенными для него функциями. Также поток представляет собой файл, и определенные функции позволяют работать с этим файлом: записывать в него и считывать... Классы потоков имеют конструкторы(как и любой нормальны класс вообще). 
    Для файловых потокв: если поток входной то конструктор ifstream, выходной ofstream, двунаправленный - fstream. еслы вызван конструктор без параметров, то создается обьект, но с файлом он не связывается. параметры этих конструкторов: имя файла char *, и режим открытия файла, второй параметр можно иногда опускать, т.к. по умолчанию для ifstream он определен ios::in, ofstream - ios::out|ios::trunc. режимы открытия описаны в структуре open_mode{}, т.е. trunc, например, удаляет файл если такой файл уже существует, т.е. при использовании ofstream of("out.txt"); если файл out.txt существует, то будет удален и создан заново, также часто используется ios::bynary - т.е. двоичный поток.
    Функции для работы с потоками(некоторые) вот: get() - возвращает символ из потока(код символа), getline(char *buffer, int num) где как можно догадаться buffer - то куда будут записываны считанные символы, num - Их количество. Аналогичная функция read(char *buffer, int num).Разница между read(...) и getlime в том, что getline помещает символ конца строки в buffer. Можно установить позицию для чтения функцией seekg(int position), узнать текущую позицию чтения потока int tellg(). - это были потоки из istream. 
    В ostream есть вот: flush() - переписывает поток на устройство, put(char c) - записывает символ в поток, seekp(int position) - установка позиции записи, tellp() - возвращает текущую позицию, write( char *buffer, int num) - записывает в поток num символов из потока buffer.
а вот маленький примерчик:
Код

#include <fstream>
#include <iostream>
using namespace std;
void main(){
    ofstream f("C:\\file.txt");
    char s[25];
    for(int i=0;i<2;i++){
        cin>>s;
        f.write(s,strlen(s));
    }
    system("pause");
    f.flush();
    system("pause");
    f.close();
    system("pause");
}

загляните в файл до вызова flush() и после - вот так оно работает. Кстати, если выкинуть flush из программы, то содержимое потока все равно перепишется в файл, также можно убрать из программы close() - все ранво закроется, и данные не будут потеряны. Предполагаю что при завершении программы вызывается что то типа fcloseall() только для потоков...

Автор: yorgan 11.6.2008, 00:37
rrrFer, Спасибо, данная книжка у меня есть, только она еще для меня сложна и времени сейчас нету изучать ее, т.к. работаю на зачет.


bsa, В очередной раз  СПАСИБО!


rrrFer,  "Повловская" у меня есть smile на досуге в метро перед зачетом почитаю. За мануал тоже спасибо.


Буду разбираться...

Автор: rrrFer 11.6.2008, 07:44
yorgan, 
отсторожно, у Павловской есть ошибка: на 274 странице она пишет, что в ostream определены функции seeg и tellg, на самом деле - seekp и tellp

Автор: yorgan 17.6.2008, 19:38
Преподаватель задал еще такой вопрос: может ли быть виртуальный конструктор? Если нет, то почему?
И еще спросил можно ли в конструкторе (или деструкторе) использовать виртуальные функции?
Подскажите пожалуйста... ответы.

Автор: JackYF 17.6.2008, 20:16
Цитата(yorgan @  17.6.2008,  18:38 Найти цитируемый пост)
Подскажите пожалуйста... ответы. 

А прочитать самому?

Автор: baldina 17.6.2008, 21:34
yorgan, конструктор не может быть виртуальным по определению: полиморфизм включается, когда точно неизвестен тип объекта (точнее, выясняется в процессе выполнения). В конструкторе тип объекта известен всегда, уже на стадии компиляции.
Из конструктора виртуальные функции технически вызывать можно, однако объект не считается полностью сконструированным и готовым для использования пока не завершился конструктор, так что в общем случае поведение при таких вызовах не определено.
А вообще-то это не настолько простая тема, что бы объяснять её в двух словах, так что - книжку в руки.

Автор: yorgan 17.6.2008, 23:07
Цитата(JackYF @  17.6.2008,  20:16 Найти цитируемый пост)
А прочитать самому?


Цитата(baldina @  17.6.2008,  21:34 Найти цитируемый пост)
так что - книжку в руки


К сожалению в книжках, которые есть у меня данные аспекты не рассматриваются и никак не комментируются (в Павловской и Березеных) :(

Автор: bsa 17.6.2008, 23:17
http://forum.vingrad.ru/books/topic-48036.html

Автор: xvr 18.6.2008, 11:23
Цитата(yorgan @ 17.6.2008,  19:38)
Преподаватель задал еще такой вопрос: может ли быть виртуальный конструктор? 

Вообще то нет, но в Borland'овском расширении С++ для Builder/Delphi - могут (только преподавателю этого не говори, не оценит  smile )
Цитата

И еще спросил можно ли в конструкторе (или деструкторе) использовать виртуальные функции?
Можно, только они не будут там виртуальными. Зато сам деструктор может быть виртуальным, часто этого достаточно.

Автор: bsa 18.6.2008, 11:36
Цитата(xvr @ 18.6.2008,  11:23)
Цитата

И еще спросил можно ли в конструкторе (или деструкторе) использовать виртуальные функции?
Можно, только они не будут там виртуальными. Зато сам деструктор может быть виртуальным, часто этого достаточно.

Я бы уточнил, что нужно явно указывать принадлежность таких методов классу (Base::method() или Derived::method()).

Автор: yorgan 18.6.2008, 15:54
Правильно ли следующее высказывание или есть что добавить?

Цитата

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

На самом деле представим, что конструктор виртуален. Это уже замах на фабрики объектов. Так что можно предположить, что в этом есть плюс. Но теперь посмотрим с другой стороны. Допустим у нас базовый класс А, и два от него дочерних: Б и В. Конструктор А виртуален, соответвенно, по правилом "наследования виртуальности" классы Б и В тоже имею виртуальные конструкторы. Теперь мы вызываем
new A(), что должен сделать компилятор? Сделать объект Б или В? Необходима какая-то параметризация. Для этого и служат фабрики, которые знают и о классе Б и о классе В. Наш класс А ничего не знает о Б и о С.

Т.о., имхо, виртуальные конструкторы не могут быть из за:
1) Неоднозначность выбора потомка.
2) Отсутствие знаний о потомках.
и главное:
3) Мы не будем знать что создаем.

Автор: JackYF 18.6.2008, 16:00
yorgan, навскидку - правильное smile

Автор: rrrFer 18.6.2008, 18:42
Цитата(yorgan @  18.6.2008,  15:54 Найти цитируемый пост)
Мы не будем знать что создаем.

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

Автор: xvr 18.6.2008, 20:04
Цитата(bsa @ 18.6.2008,  11:36)
Цитата(xvr @ 18.6.2008,  11:23)
Цитата

И еще спросил можно ли в конструкторе (или деструкторе) использовать виртуальные функции?
Можно, только они не будут там виртуальными. Зато сам деструктор может быть виртуальным, часто этого достаточно.

Я бы уточнил, что нужно явно указывать принадлежность таких методов классу (Base::method() или Derived::method()).

При явном указании принадлежности (через :: ) виртуальности не будет не только в конструкторах и деструкторах  smile Если же из конструктора или деструктора вызвать виртуальную функцию не непосредственно (например так)
Код

class Test {
public:

 virtual ~Test() {func1();}

 void func1() {func2();}
 virtual void func2() {}
};

class Test2 : public Test {
public:
 ~Test2() {}

 virtual void func2() {}
};

Test* t=new Test2;
t->func1(); // Test::func1, Test2::func2
delete t; // Test2::~Test2, Test::~Test, Test::func1, Test::func2
то деструкторе Test2 будет вызванны функции Test::func1 и Test::func2. При вызове t->func1() будут вызванны Test::func1 и Test2::func2

Автор: bsa 18.6.2008, 20:24
Цитата(xvr @ 18.6.2008,  20:04)
то деструкторе Test2 будет вызванны функции Test::func1 и Test::func2. При вызове t->func1() будут вызванны Test::func1 и Test2::func2

отсюда вывод - нельзя вызывать виртуальные методы из конструктора и деструктора (если только через явное указание принадлежности классу).

Автор: xvr 19.6.2008, 11:32
Цитата(bsa @ 18.6.2008,  20:24)
Цитата(xvr @ 18.6.2008,  20:04)
то деструкторе Test2 будет вызванны функции Test::func1 и Test::func2. При вызове t->func1() будут вызванны Test::func1 и Test2::func2

отсюда вывод - нельзя вызывать виртуальные методы из конструктора и деструктора (если только через явное указание принадлежности классу).

Вывод неправильный - 
Draft 2005, 12.7 p4
Цитата

Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2). When a
virtual function is called directly or indirectly from a constructor (including from the mem-initializer for a data member)
or from a destructor, and the object to which the call applies is the object under construction or destruction, the function
called is the one defined in the constructor or destructor’s own class or in one of its bases, but not a function overriding
it in a class derived from the constructor or destructor’s class, or overriding it in one of the other base classes of the most
derived object (1.8). If the virtual function call uses an explicit class member access (5.2.5) and the object-expression
refers to the object under construction or destruction but its type is neither the constructor or destructor’s own class or
one of its bases, the result of the call is undefined. [ Example:
class V {
public :
virtual void f();
virtual void g();
};
class A : public virtual V {
public :
virtual void f();
};
class B : public virtual V {
public :
virtual void g();
B(V*, A*);
};
class D : public A , B {
public :
virtual void f();
virtual void g();
D () : B((A*) this , this ) { }
};
B::B(V* v , A* a) {
f (); / / calls V::f, not A::f
g (); / / calls B::g, not D::g
v->g (); / / v is base of B, the call is well-defined, calls B::g
a->f (); / / undefined behavior, a’s type not a base of B
}
—end example ]

Автор: bsa 19.6.2008, 21:04
xvr, т.е. получается, что таблица виртуальных методов обновляется прямо перед входом в тело конструктора, но после инициализации всех членов класса. А в деструкторе с точностью до наоборот - после выхода из тела деструктора восстанавливается таблица класса предка. Правильно ли я понял?

Автор: mes 19.6.2008, 21:24
Цитата(bsa @  19.6.2008,  21:04 Найти цитируемый пост)
xvr, т.е. получается, что таблица виртуальных методов обновляется прямо перед входом в тело конструктора, но после инициализации всех членов класса. А в деструкторе с точностью до наоборот - после выхода из тела деструктора восстанавливается таблица класса предка. Правильно ли я понял? 

что то я запутался, но нижеприведенный пример  выдает А:f1() A::f2() , что на мой взгляд не соотвествует написанному.. прошу пояснения 
Код

#include <cstdlib>
#include <iostream>

class A
{
    public:
        A() { f1(); }
        virtual ~A() { f2(); }

        virtual void f1() { std::cout << "A::f1()" << std::endl; };
        virtual void f2() { std::cout << "A::f2()" << std::endl; };
};
class A1: public A
{
    public:
        A1() {  }
       ~A1() {  }
        virtual void f1() { std::cout << "A1::f1()" << std::endl; };
        virtual void f2() { std::cout << "A1::f2()" << std::endl; };

};


int main()
{
    A1* pa = new A1();
    delete pa;

   system("pause");
    return 0;
}



Добавлено через 1 минуту и 43 секунды
а вобше разве это не UB ?

Автор: bsa 19.6.2008, 21:37
mes, в том то и дело, что соответствует. Когда выполняется конструктор класса A, класс A1 еще не сконструировался и таблица виртуальных методов содержит указатели на методы класса А, который уже начал конструироваться, когда дело доходит до деструктора класса А, то класс А1 полностью деинициализировался и восстановил указатели на виртуальные методы класса А (ну или деструктор класса А это сделал перед входом в тело).

Автор: xvr 20.6.2008, 07:41
Цитата(bsa @ 19.6.2008,  21:04)
xvr, т.е. получается, что таблица виртуальных методов обновляется прямо перед входом в тело конструктора, но после инициализации всех членов класса. А в деструкторе с точностью до наоборот - после выхода из тела деструктора восстанавливается таблица класса предка. Правильно ли я понял?

Да. (Хотя технически таблицу виртуальных методов в деструкторе восстанавливает сам деструктор при входе, а не наследник при выходе, впрочем результат одинаковый)

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)