Oto co mam na myśli:
class MyClass {
int arr1[100];
int arr2[100];
int len = 100;
void add(int* x1, int* x2, int size) {
for (int i = 0; i < size; i++) {
x1[i] += x2[i];
}
}
};
int main() {
MyClass myInstance;
// Fill the arrays...
myInstance.add(myInstance.arr1, myInstance.arr2, myInstance.len);
}
add
może już uzyskać dostęp do wszystkich potrzebnych zmiennych, ponieważ jest to metoda klasowa, więc czy to zły pomysł? Czy istnieją powody, dla których powinienem lub nie powinienem tego robić?
object-oriented
c++
class-design
methods
encapsulation
papierowy mężczyzna
źródło
źródło
add
metodę bez argonu , która działa bezpośrednio na jej wewnętrznych elementach ? Dlaczego?add
metodę, która pobiera argumenty, ale nie istnieje jako część klasy. Tylko czysta funkcja dodawania dwóch tablic razem.Odpowiedzi:
Z klasą mogą być rzeczy, które zrobiłbym inaczej, ale odpowiadając na bezpośrednie pytanie, moja odpowiedź byłaby
tak, to zły pomysł
Moim głównym powodem jest to, że nie masz kontroli nad tym, co jest przekazywane do
add
funkcji. Pewnie masz nadzieję, że jest to jedna z tablic składowych, ale co się stanie, jeśli ktoś przejdzie w innej tablicy, która ma mniejszy rozmiar niż 100, lub jeśli przejdziesz przez długość większą niż 100?Dzieje się tak, ponieważ stworzyłeś możliwość przekroczenia bufora. I to wszystko jest złe.
Aby odpowiedzieć na kilka (dla mnie) oczywistych pytań:
Mieszasz tablice w stylu C z C ++. Nie jestem guru C ++, ale wiem, że C ++ ma lepsze (bezpieczniejsze) sposoby obsługi tablic
Jeśli klasa ma już zmienne składowe, dlaczego musisz je przekazać? To bardziej pytanie architektoniczne.
Inne osoby z większym doświadczeniem w C ++ (przestałem go używać 10 lub 15 lat temu) mogą mieć bardziej wymowne sposoby wyjaśniania problemów i prawdopodobnie wymyślą również więcej problemów.
źródło
vector<int>
byłoby bardziej odpowiednie; długość byłaby wówczas bezużyteczna. Alternatywnieconst int len
, ponieważ tablica o zmiennej długości nie jest częścią standardu C ++ (chociaż niektóre kompilatory ją obsługują, ponieważ jest to opcjonalna funkcja w C).std::array
nie ulega rozkładowi podczas przekazywania jej do parametrów, ma wygodniejszy sposób na uzyskanie jej rozmiaru, jest kopiowalna i ma dostęp z opcjonalnym sprawdzaniem granic, nie powodując przy tym narzutu Tablice w stylu C.Wywołanie metody klasy z niektórymi zmiennymi klasy niekoniecznie jest złe. Ale robienie tego spoza klasy jest bardzo złym pomysłem i sugeruje zasadniczą wadę w twoim projekcie OO, a mianowicie brak odpowiedniej enkapsulacji :
len
jest to długość tablicy i konsekwentnie go używać. Jest to sprzeczne z zasadą najmniejszej wiedzy . Taka zależność od wewnętrznych szczegółów klasy jest wyjątkowo podatna na błędy i ryzykowna.len
na bardziej nowoczesnystd::vector<int>
), ponieważ wymagałoby to również zmiany całego kodu za pomocą klasy.MyClass
obiektach, niszcząc niektóre publiczne zmienne bez przestrzegania reguł (tzw. Niezmienniki klas )Powinieneś refaktoryzować swój kod i:
private
lubprotected
, chyba że istnieje dobry powód, aby tego nie robić.object.action(additional parameters for the action)
.:).źródło
Jeśli celem tego projektu jest chęć ponownego użycia tej metody dla danych, które nie pochodzą z tego wystąpienia klasy, możesz zadeklarować ją jako
static
metodę .Metoda statyczna nie należy do żadnej konkretnej instancji klasy. Jest to raczej metoda samej klasy. Ponieważ metody statyczne nie są uruchamiane w kontekście żadnej konkretnej instancji klasy, mogą one uzyskiwać dostęp tylko do zmiennych składowych, które są również zadeklarowane jako statyczne. Biorąc pod uwagę, że twoja metoda
add
nie odwołuje się do żadnej ze zmiennych składowych zadeklarowanych wMyClass
, jest dobrym kandydatem do uzyskania deklaracji jako statyczna.Jednak problemy bezpieczeństwa wymienione w innych odpowiedziach są ważne: Nie sprawdzasz, czy obie tablice są co najmniej
len
długie. Jeśli chcesz pisać nowoczesny i solidny C ++, powinieneś unikać używania tablic. Wstd::vector
miarę możliwości używaj klasy . W przeciwieństwie do zwykłych tablic, wektory znają swój rozmiar. Więc nie musisz sam śledzić ich wielkości. Większość (nie wszystkie!) Ich metod wykonuje również automatyczne sprawdzanie granic, co gwarantuje, że otrzymasz wyjątek, gdy czytasz lub piszesz poza końcem. Regularny dostęp do tablicy nie wykonuje sprawdzania ograniczeń, co w najlepszym wypadku skutkuje segfaultem, a w gorszym przypadku może spowodować podatność na przepełnienie bufora .źródło
Metoda w klasie idealnie manipuluje enkapsulowanymi danymi wewnątrz klasy. W twoim przykładzie nie ma powodu, aby metoda add miała jakiekolwiek parametry, wystarczy użyć właściwości klasy.
źródło
Przekazywanie (części) obiektu do funkcji składowej jest w porządku. Wdrażając swoje funkcje, zawsze musisz mieć świadomość możliwego aliasingu i jego konsekwencji.
Oczywiście umowa może pozostawiać niektóre aliasy niezdefiniowane, nawet jeśli język to obsługuje.
Wybitne przykłady:
Z drugiej strony zadanie samodzielnego poruszania się, chociaż nie powinno eksplodować w twoją twarz, nie musi być przeszkodą.
v.push_back(v[2]);
.std::copy()
zakłada, że miejsce docelowe nie zaczyna się w źródle.Ale twój przykład ma inne problemy:
this
. Działa jak darmowa funkcja użyteczności, która po prostu znajduje się w niewłaściwym miejscu.Podsumowując: Tak, ten przykład jest zły. Ale nie dlatego, że funkcja członka otrzymuje przekazywane elementy członkowskie.
źródło
W szczególności zrobienie tego jest złym pomysłem z kilku dobrych powodów, ale nie jestem pewien, czy wszystkie z nich są integralną częścią twojego pytania:
vector<int>
ułatwiłoby ci życie.źródło
Jest to klasyczne podejście „C z klasami” do C ++. W rzeczywistości nie tak napisałby każdy doświadczony programista C ++. Po pierwsze, używanie surowych tablic C jest ogólnie odradzane, chyba że implementujesz bibliotekę kontenerów.
Coś takiego byłoby bardziej odpowiednie:
źródło