C Plus Plus
Advertisement

Jeśli funkcja lub metoda rzuca wyjątkiem to w deklaracji możemy poinformować użytkowników o typach wyjątków rzucanych z wnętrza funkcji/metody. Informację taką nazywamy specyfikacją wyjątków. Np.:

int foo(int a, int b) throw(std::exception)

W linii powyżej deklarujemy funkcje foo o 2 parametrach typu int, która zwraca wartość int i może rzucić wyjątkiem typu std::exception. Zadaniem kompilatora jest zagwarantować, że funkcja ta nie rzuci żadnym innym typem. Oczywiście nic nie stoi na przeszkodzie abyśmy w definicji foo napisali:

int foo(int a, int b) throw(std::exception)
{
	if(a<b)
	{
		throw int(2);
	}
	return a+b;
}

W przypadku gdy a<b rzucany jest wyjątek typu int, kompilator generuje kod, który sprawdza typ rzuconego wyjątku, w przypadku, gdy wyjątek nie jest typu std::exception wywoływana jest funkcja unexpected, która wywołuje funkcję terminate. Funkcja terminate kończy działanie programu. Ponieważ zadaniem kompilatora jest zagwarantowanie, że poza std::exception nie zostanie rzucony wyjątek innego typu musi on wygenerować kod podobny do tego poniżej:

void foo(int a, int b) throw(std::exception)
{
	try
	{
		if(a<b)
		{
			throw int(2);
		}
		return a+b;
	}
	catch(std::exception &e)
	{
		throw;
	}
	catch(..)
	{
		std::unexpected();
	}
}

Jak widać na przykładzie powyżej narzut związany ze specyfikacją wyjątków może być całkiem spory, dlatego ze specyfikacji wyjątków należy korzystać tylko wtedy, gdy chcemy poinformować przyszłych użytkowników, że nasza funkcja/metoda nie rzuci żadnego wyjątku:

int foo(int a, int b) throw();

Podstawowe minusy związane ze specyfikowaniem wyjątków przez funkcje to:

  1. pogorszenie czasu wykonania funkcji,
  2. w przypadku szablonów nie jest możliwe napisanie sensownej specyfikacji wyjątków, ponieważ nie wiemy, jakie wyjątki mogą zostać rzucone przez typy na których operuje nasz szablon,
  3. niektóre kompilatory nie będą potrafiły zoptymalizować kodu funkcji/metody ze specyfikacją wyjątków.
  4. jeśli dziedziczymy z klasy, która wyspecyfikowała wyjątki to nie możemy tej specyfikacji rozszerzać, możemy ją tylko zawężać.

Jak duży narzut wiąże się ze specyfikacją wyjątków(dla danego kompilatora) bardzo łatwo sprawdzić poniższym programem(dla gcc 3.3.1 jest to prawie 3 razy):

#include <iostream>
#include <stdexcept>
#include <time.h> //#include <ctime>

using namespace std;

int test1(int a, int b) throw (std::exception)
{
  if(a<0)
    throw std::exception();
  if(a>b)
    return 0;
  a = 0;
  a *= b;
  a += b;
  return a;
}

int test2(int a, int b)
{
  if(a<0)
    return 0;

  if(a>b)
    return 0;
  a = 0;
  a *= b;
  a += b;
return a;
}

int test3(int a, int b)
{
  if(a<0)
    throw std::exception();
  if(a>b)
    return 0;
  a = 0;
  a *= b;
  a += b;
return a;
}

int main(int argc, char *argv[])
{
  const int MAX = 60000000L;
  int i;
  clock_t t1,t2;
  t1 = clock();
  for(i=0; i<MAX; ++i)
  {
    test1(i, i+1);
  }
  t2 = clock();

  cout<<static_cast<double>(t2-t1)/CLOCKS_PER_SEC<<"s"<<endl;

  t1 = clock();
  for(i=0; i<MAX; ++i)
  {
    test2(i, i+1);
  }

  t2 = clock();
  cout<<static_cast<double>(t2-t1)/CLOCKS_PER_SEC<<"s"<<endl;
  t1 = clock();
  for(i=0; i<MAX; ++i)
  {
    test3(i, i+1);
  }
  t2 = clock();
  cout<<static_cast<double>(t2-t1)/CLOCKS_PER_SEC<<"s"<<endl;

  system("PAUSE");
  return 0;
}

A to wynik(PIV 2.8):

1.828s
0.719s
0.719s
Advertisement