Plik nagłówkow, enum i spółka

0

Hej,
Jestem w trakcie kursu C/C++ na studiach i spotykam się ze sporymi nowościami mimo że myślałem że rozumiem czyste początki,
więc mam do was kilka pytań, na samym dole zamieszczę kod.
Pytania:

  1. Nie rozumiem sposobu działania enum w tym programie, przecież enum istnieje tylko i wyłącznie w pliku nagłówkowym, więc powinien służyć jedynie jako prototyp/deklaracja, nie jestem w stanie zrozumieć jak ma to działać dokładniej, bo przecież w wypadku funkcji deklarujemy jedynie ją w pliku nagłówkowym, a cała funkcja jest rozpisana w odpowiednim pliku z rozszerzeniem .cpp. Jeśli przy okazji ktoś chciałby mi wytłumaczyć czemu dodajemy typ usigned int do tego enum też byłoby miło.
  2. W jakim celu podpinamy/załączamy: #include "calculator.h" do pliku calculator.cpp, ja tworzyłem prostsze przykłady, to nie robiłem tego i działało, to się nie załącza automatycznie przez trywialnie mówiąc tą samą nazwę?
  3. Ostatnie moje pytanie załącza się z brakiem zrozumienia static_cast, nie rozumiem jak do końca to działa.

W wielkim skrócie zrobiłem program na szablonie przygotowanym przez wykładowcę, a nie rozumiem części szablonu i dlatego przychodzę z pytaniami, koncept do zajęć jest niestety tragicznie napisany.

Z góry wielkie dzięki za każdą pomoc (;

//Plik main.cpp
#include <iostream>

#include "calculator.h"

int main() {
	float x, y, result;
	std::cout << "Type two numbers." << std::endl << "x: ";
	std::cin >> x;
	std::cout << std::endl << "y: ";
	std::cin >> y;
	std::cout << std::endl << "Choose arithmetic operation to be performed:" << std::endl
		<< "0 - addition" << std::endl
		<< "1 - subtraction" << std::endl
		<< "2 - multiplication" << std::endl
		<< "3 - division" << std::endl;
	unsigned int operationValue;
	std::cin >> operationValue;
	Operation operation = static_cast<Operation>(operationValue);
	result = calculate(operation, x, y);
	std::cout << "Operation result equals: " << result << std::endl;
	return EXIT_SUCCESS;
}

//Plik calculator.h
#pragma once

#include <limits>

enum class Operation : unsigned int {
	ADD = 0,
	SUBTRACT = 1,
	MULTIPLY = 2,
	DIVIDE = 3,
	INVALID = std::numeric_limits<unsigned int>::max()
};

float add(float x, float y);
float subtract(float x, float y);
float multiply(float x, float y);
float divide(float x, float y);

float calculate(Operation operation, float x, float y);

//Plik calculator.cpp
#include <iostream>

#include "calculator.h"

namespace {
	
	void quitWithError() {
		std::cout << "Invalid operation performed" << std::endl;
		exit(EXIT_FAILURE);
	}
	
} // anonymous namespace


float add(float x, float y){
	return x+y;
}

float substract(float x, float y){
	return x-y;
}

float multiply(float x, float y){
	return x*y;
}

float divide(float x, float y){
	if(y==0)
	{
		quitWithError();
	}
	return x/y;
}

float calculate(Operation operation, float x, float y){
	switch(operation){
		case Operation::ADD:
			return add(x, y);
			break;
		case Operation::SUBTRACT:
			return substract(x, y);
			break;
		case Operation::MULTIPLY:
			return multiply(x ,y);
			break;
		case Operation::DIVIDE:
			return divide(x, y);
			break;
	}	
}

0

Ad 1) Zadaj sobie pytanie jaki rozmiar ma enum jak nie podasz "usigned int"
Ad 2) Zastanów się czym sie róźni plik cpp od h , i które pliki są kompilowane
a potem linkowane do EXE
Ad 3) A jak chiałbyś zamienić unsigned int na enum ?

4

Nie rozumiem sposobu działania enum w tym programie, przecież enum istnieje tylko i wyłącznie w pliku nagłówkowym

Inkludujesz zawartość calculator.h do main.cpp więc wklejasz deklarację do maina. #include to tylko prosta przeklejka zawartości jednego pliku do drugiego więc jeśli robisz include to cała zawartość nagłówka jest przeklejona do do pliku gdzie zrobiłeś include.

Jeśli przy okazji ktoś chciałby mi wytłumaczyć czemu dodajemy typ usigned int do tego enum też byłoby miło

To jest definicja underlying-type, domyślnie jest to int. Definiując go jawnie, wartości enuma podlegają wtedy takim samym zasadom rzutowania jak tenże zdefiniowany typ. Pisząc enum class : unsigned int "mówisz": wartości mojego enuma są typu unsigned int i muszą być jawnie rzutowane.

W jakim celu podpinamy/załączamy: #include "calculator.h" do pliku calculator.cpp, ja tworzyłem prostsze przykłady, to nie robiłem tego i działało

Niestatyczna definicja potrzebuje deklaracji, więc musisz wkleić deklaracje do calculator.cpp żeby kompilator mógł sprawdzić czy wszystko się zgadza a później móc egzekwować ODR (one definition rule). Czemu inny kod niż podany działał to musiałbym zobaczyć ten kod, nie jestem jasnowidzem.

Ostatnie moje pytanie załącza się z brakiem zrozumienia static_cast, nie rozumiem jak do końca to dział

Na temat rzutowania są pisane całe artykuły, lepiej będzie jak poczytasz dokumentację najpierw i potem wrócisz z konkretnymi pytaniami. W Twoim przypadku potrzebujesz static_cast bo definiując swój enumerator jako enum class wyłączasz możliwość niejawnego rzutowania.

3

ad1)
Starty enum (bez class) miał kilka wad.

  • rozmiar zmienej na tego enuma, zależy od ilości/wartość zadeklarowanych wartości w tym enum, co powadziło do problemów w forward delclaration.
  • wartości tego enema działał na ten sam scope co typ enuma - przez co be zapobiec kolizjom oznaczeń robiło sie "smurfcoding"
  • następowała domyślna konwersja międzu enum do int, co prowadziło do pomyłek.

żeby pozbyć się tych wad w C++11 prowadzono enum class, w którym możne określić "underlaing type" u cibie jest to unsigned int dymyślnie jest to int.
Ergo ow enmum class:

  • ma dobrze zdefuiniowany rozmiar (mozna be zproblemu zrobić forward declare: enum class Operation : unsigned int; - zdefiniować typ euma bez definiowania wartości)
  • żeby odnieść się do wartości trzeba, użyć nazwy typu, co poprawia czytelność
  • nie ma już domyślnej konwersji doint lub "underlying type", jeśli konwersja jest potrzenam to nalerzy użyc static_cast

1 użytkowników online, w tym zalogowanych: 0, gości: 1