Программирование на C++

10 способов прострелить себе ногу

Создание веб приложений

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

Подпись для третьего не придумал

Американец изнасиловал лошадь, потому что думал, что у них родится кентавр

Помощь обездоленным якутам на дальнем севере

Благотворительность (на правах рекламы)

Показаны сообщения с ярлыком Особенности CPP. Показать все сообщения
Показаны сообщения с ярлыком Особенности CPP. Показать все сообщения

пятница, 25 января 2013 г.

Перегрузка оператора вызова функций operator () в С++


Оператор вызова функций должен быть перегружен только как нестатический член класса!
Формат: 
тип operator () (список_параметров), где список параметров может быть неопределенным и значения могут быть определены по умолчанию.

Оператор вызова функций применяется в том случае, когда нужно использовать объект как функцию, в этом случае такие объекты называются объектами-функциями или функторами (functors). Рассмотрим пример на классе Point (точка):

class Point{
private:
 int x;
 int y;
public:
 Point(int x, int y){
  this->x=x;
  this->y=y;
 }
 Point() {}
 Point operator()(int i,int j){
  x=i;
  y=j;
  return *this;
 }
 Point operator+(Point obj){
  Point temp;
  temp.x = obj.x + x;
  temp.y = obj.y + y;
  return temp;
 }
 void show(){
  cout< < "x: "< < x< < " y: "< < y< < endl;
 }
};

int _tmain(int argc, _TCHAR* argv[])
{
 Point obj1(10, 20), obj2(1, 1); //вызываются конструкторы формата Point(x,y)

 obj1.show();
 obj1(7, 8); // вызывается оператор() который меняет наши значения.
 obj1.show();

 obj1 = obj2 + obj1(10, 10); // может быть использовано в выражениях
 obj1.show();

 getchar();
 return 0;
}
Результаты:
10,20
7,8
11,11

В другом случае нам пришлось бы писать функцию, где нужно было бы указывать значения. Один минус этого примера в том, что можно спутать оператор () с конструктором... 
Зато благодаря этой перегрузки можно написать такой код:

list  lst;
//инициализируем весь список
for_each(lst.begin(),lst.end(), Point(1,1));
///теперь все значения точек в списке поменяются на 1,1
///отличный способ использования stl

Также перегрузка этого оператора применяется в задаче индексирования многомерного массива и в задаче выделения подстроки.

Объект функция имеет такими преимуществами перед обычными функциями:

  • Каждому объекту функции соответствует свой тип.
  • Объект функции можно инициализировать на стадии выполнения перед ее вызовом.


суббота, 19 января 2013 г.

Проблема линковщика с шаблонами в С++

Б-же, как меня з%?%"а эта проблема, убил на нее 2.5 часа, ни один преподаватель в вузе не сказал про эту проблему. Чувствую butthurt. Надеюсь, теперь вы не совершите такую же ошибку...

Предположим, что у нас есть класс (ОН находится в .h файле)

template < typename T>
class Single_Linked_List
{
private:
 Node *head;
 Node *tail;
 Node *current;
 unsigned size;
public:
        void Push_Front(const T& element);
 void Push_Back(const T& element);
 T Pop_Front();
 T Pop_Back();
////и дохрена методов внизу с конструктором деструктором
};

У нас есть .cpp файл, где находится описание всех методов итд:

#include "Single_Linked_List.h"
////
описание всех методов

Теперь мы попробуем потестить наш класс односвязного списка:

        Single_Linked_List<int> sll;
 sll.Push_Front(1);
 sll.Push_Front(1);

ОШИБКА -_-, в чем проблема ?
error LNK2019: unresolved external symbol "public: __thiscall Single_Linked_List::~Single_Linked_List(void)" (??1?$Single_Linked_List@H@@QAE@XZ) referenced in function _wmain 1>ListE.obj : error LNK2019: unresolved external symbol "public: void __thiscall Single_Linked_List::Push_Front(int const &)" (?Push_Front@?$Single_Linked_List@H@@QAEXABH@Z) referenced in function _wmain 1>ListE.obj : error LNK2019: unresolved external symbol "public: __thiscall Single_Linked_List::Single_Linked_List(void)" (??0?$Single_Linked_List@H@@QAE@XZ) referenced in function _wmain 1> : fatal error LNK1120: 3 unresolved externals
Видно, что компилятор ПРОСТО не видит .cpp файл. Теперь, подключим в main.cpp вместо .h файла, .cpp файл. О, ЧУДО, оно работает БЛ%^#АТЬ.
Давайте еще подробнее разберемся что к чему:

Заголовочный файл (header - .h) - файл, где находится описание, но не реализация.
.cpp - файл, где находится реализация. На самом деле они ничем не отличаются, разделение на .h и .cpp файлы - это просто формальность, для того, чтобы отличить, где реализация, а где описание. При компиляции, компилятор включает все include файлы в единую единицу трансляции и делают из него .obj файл. Потом из всех .obj файлов собирается программа. Если в .obj файле есть 1 реализация функции func, то она будет видна и в других .obj файлах, НО, если реализаций много, то будет ошибка линковщика.
В нашем случае реализацию шаблонной функции можно инстанцировать (генерирование типов и функций) множеством способов (func<int>, func<float>, func<double>...) поэтому будет ошибка.

Итак, эту проблему можно решить несколькими способами:

  1. Подключать в main.cpp не .h файла, а его реализацию .cpp файл.
  2. Всю реализацию хранить вместе с описанием (.h или .hpp файлы)
  3. Подключить в конце .h файла .cpp файл.
  4. Использовать export template (не поддерживается многими компиляторами, в том числе VS2010, поэтому я не смогу его продемонстрировать, он реализован только в Comeau)
  5. Просто написать объявление всех вариантов использования списка в cpp файле, например: template class Single_Linked_List<int>; потом с float и т. д.
  6. Использовать глобальную функцию, где опробовать все используемые методы в main'е, например (сделать только объявление глобальной функции в .h и все): 
void TemporaryFunction ()
{
 Single_Linked_List sll;
 sll.Push_Front(0);
 sll.Display();
}

После того, как в .h файле мы сделали такую функцию, теперь в main.cpp работают:
конструктор, деструктор, Push_Front, Display. С другими методами будет ошибка.

Из всех вариантов мне нравится третий, так как он не нарушает самой идеи раздельной компиляции и прост в понимании и реализации. Есть ли есть варианты, добавления и так далее, пишите в комментарии, всем, спасибо, за внимание :)

среда, 16 января 2013 г.

Перегрузка оператора [] C++


Сегодня я рассмотрю пример перегрузки оператора для класса, где одним из полей является массив шаблонного типа, а сам класс-шаблонный.
Вспомним какие перегруженные операторы должны быть обязательно членами класса, какие нет.
Из таблицы видно, что оператор [] должен быть обязательно членом класса, то есть никаких friend'ов и так далее. Вспомним, что, если какой-то operator (оператор) является членом класса, то this является неявным аргументом.
Например, если оператор+ член класса, то мы его записываем так:
R T::operator +(S b);
Если, friend, то:
R operator +(S a, T b);

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

Класс:

template < typename Type>
class A {
private:
 Type *mas;
 unsigned _size;
public:
 A (unsigned size){
  _size=size;
  mas=new Type[_size];
  for (unsigned i=0; i< _size; i++){
   mas[i]=i;
  }
 }
 Type& operator[](const unsigned index) const;
};
Теперь само определение перегруженного оператора:

template < typename Type> Type& A< Type>::operator[](const unsigned index) const{
 try { //поискать ошибку
  if (index< 0||index>_size){
   throw("out of range"); //вбросить
  }
  return mas[index]; //возвратить элемент
 }
 catch(const char *msg){ //поймать ошибку
  puts(msg);
  exit(1);
 }
}
Код для проверки:

unsigned x;
cout< < "Enter size of mas: ";
cin>>x;
A< unsigned> a(x);
cout< < endl;
for (unsigned i=0; i< x; i++){
 cout< < "Element: "< < i< < "  "< < a[i]< < endl;
}
Демонстрация: