클래스 변수를 전달하는 클래스 메소드를 작성하는 것이 나쁜 생각입니까?


14

여기 내가 의미하는 바가있다 :

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클래스 메소드이기 때문에 필요한 모든 변수에 이미 액세스 할 수 있으므로 나쁜 생각입니까? 내가 이것을해야하거나하지 말아야 할 이유가 있습니까?


13
이 작업을 수행하고 싶다고 생각하면 나쁜 디자인을 암시하는 것입니다. 클래스의 속성은 아마도 다른 곳에 속할 것입니다.
bitsoflogic

11
스 니펫이 너무 작습니다. 이 크기의 혼합에서는이 수준의 혼합 추상화가 발생하지 않습니다. 디자인 고려 사항에 대한 좋은 텍스트로이 문제를 해결할 수도 있습니다.
Joshua

16
솔직히 왜 당신이 이것을 할 것인지 이해하지 못합니다. 무엇을 얻습니까? 왜 add내부에서 직접 작동하는 인수없는 방법이 없는가? 왜?
이안 켐

2
@IanKemp 또는 add인수를 취하지 만 클래스의 일부로 존재하지 않는 메소드가 있습니다. 두 배열을 함께 추가하기위한 순수한 함수 일뿐입니다.
Kevin

add 메소드는 항상 arr1과 arr2를 추가 합니까 , 아니면 아무것도 추가하는 데 사용할 수 있습니까?
user253751

답변:


33

수업에 다른 방식으로 할 수있는 일이있을 수 있지만, 직접 질문에 대답하려면 내 대답은

네, 나쁜 생각입니다

이것에 대한 나의 주요 이유는 add함수에 전달되는 것을 제어 할 수 없기 때문입니다 . 물론 그것이 멤버 배열 중 하나 가기를 바랍니다. 그러나 누군가가 100보다 작은 크기의 다른 배열을 통과하거나 100보다 큰 길이를 전달하면 어떻게됩니까?

버퍼 오버런 가능성이 생겼습니다. 그리고 그것은 사방에 나쁜 것입니다.

좀 더 명확한 질문에 대답하려면 :

  1. C 스타일 배열을 C ++와 혼합하고 있습니다. 나는 C ++ 전문가가 아니지만 C ++이 배열을 처리하는 더 나은 (safer) 방법을 가지고 있음을 알고 있습니다

  2. 클래스에 이미 멤버 변수가있는 경우 왜 전달해야합니까? 이것은 건축 문제에 관한 것입니다.

더 많은 C ++ 경험을 가진 다른 사람들 (10 년 또는 15 년 전에 사용을 중단했습니다)은 문제를 설명하는 더 설득력있는 방법을 가질 수 있으며 더 많은 문제가 발생할 수도 있습니다.


실제로 vector<int>더 적합 할 것입니다. 그러면 길이는 쓸모가 없습니다. 또는 const int len가변 길이 배열은 C ++ 표준의 일부가 아니기 때문에 (C의 선택적 기능이기 때문에 일부 컴파일러가 그것을 지원하지만).
Christophe

5
@Christophe Author는 고정 크기 배열을 사용했습니다.이 배열 std::array은 매개 변수로 전달 할 때 부패하지 않고 크기를 가져 오는보다 편리한 방법이 있으며 복사 가능하며 선택적 경계 검사를 통해 액세스 할 수 있으며 오버 헤드는 없습니다. C 스타일 배열.
큐빅

1
@Cubic : 임의의 고정 크기 (100과 같은)로 배열을 선언하는 코드를 볼 때마다 저자 가이 말도 안되는 의미에 대해 전혀 모르는 초보자라고 확신하며 그를 추천하는 것이 좋습니다 이 디자인을 다양한 크기의 것으로 변경합니다.
Doc Brown

배열은 괜찮습니다.
Monica와의 가벼움 경주

... 내 말의 의미를 이해하지 못한 사람들을 위해 Zero One Infinity Rule
Doc Brown

48

일부 클래스 변수로 클래스 메소드를 호출하는 것이 반드시 나쁘지는 않습니다. 그러나 클래스 외부에서 그렇게 하는 것은 매우 나쁜 생각 이며 OO 디자인의 근본적인 결함, 즉 적절한 캡슐화 가 없음을 나타냅니다 .

  • 클래스를 사용하는 모든 코드 len는 그것이 배열의 길이 임을 알고 일관되게 사용해야합니다. 이것은 최소한의 지식 원칙에 위배 됩니다. 클래스의 내부 세부 사항에 대한 이러한 종속성은 오류가 발생하기 쉽고 위험합니다.
  • 이것은 클래스의 진화를 매우 어렵게 만듭니다 (예를 들어 언젠가 레거시 배열을 변경하고 len더 현대적인 방식으로 std::vector<int>변경하려는 경우). 클래스를 사용하여 모든 코드를 변경해야하기 때문입니다.
  • 코드의 어떤 부분이라도 규칙을 따르지 MyClass않고 일부 공용 변수를 손상 시켜 객체 를 혼란스럽게 만들 수 있습니다 ( 클래스 불변량 이라고 함 )
  • 마지막으로, 메소드는 매개 변수와 만 작동하고 다른 클래스 요소에 의존하지 않기 때문에 실제로 클래스와 독립적입니다. 이런 종류의 메소드는 클래스 외부의 독립적 인 함수 일 수 있습니다. 또는 적어도 정적 방법.

코드를 리팩터링해야합니다.

  • 클래스 변수를 만들 private거나 protected그것을 할 안 좋은 이유가 아니라면.
  • 수업에서 수행 할 행동으로 공개 방법을 설계하십시오 (예 :) object.action(additional parameters for the action).
  • 이 리팩토링 후에도 클래스 변수로 호출해야하는 클래스 메소드가 여전히있는 경우 공용 메소드를 지원하는 유틸리티 함수인지 확인한 후 보호 또는 개인화하십시오.

7

이 디자인의 의도가이 클래스 인스턴스에서 제공되지 않은 데이터에 대해이 메소드를 재사용하려는 경우 static메소드 로 선언 할 수 있습니다 .

정적 메소드는 클래스의 특정 인스턴스에 속하지 않습니다. 오히려 클래스 자체의 방법입니다. 정적 메소드는 클래스의 특정 인스턴스 컨텍스트에서 실행되지 않으므로 정적으로 선언 된 멤버 변수에만 액세스 할 수 있습니다. 메소드 add가에 선언 된 멤버 변수를 참조하지 않는다는 점을 고려하면 MyClass정적으로 선언하기에 적합한 후보입니다.

그러나 다른 답변에서 언급 한 안전 문제는 유효합니다. 두 배열이 모두 len긴지 확인하고 있지 않습니다 . 현대적이고 강력한 C ++를 작성하려면 배열 사용을 피해야합니다. std::vector가능할 때마다 대신 수업을 사용하십시오 . 일반 배열과 달리 벡터는 자체 크기를 알고 있습니다. 따라서 크기를 직접 추적 할 필요가 없습니다. 대부분의 메소드는 자동 바운드 검사를 수행하므로 끝 부분을 읽거나 쓸 때 예외가 발생합니다. 정기적 인 배열 액세스는 바운드 검사를 수행하지 않으므로 segfault가 최대가되고 악용 가능한 버퍼 오버플로 취약점 이 발생할 수 있습니다 .


1

클래스의 메소드는 클래스 내부의 캡슐화 된 데이터를 이상적으로 조작합니다. 예제에서 add 메소드가 매개 변수를 가질 이유가 없습니다. 단순히 클래스의 특성을 사용하십시오.


0

멤버 함수에 객체를 전달하는 것은 좋습니다. 당신의 기능을 구현하는 경우, 당신은 항상수 앨리어싱을 인식 하고, 그 결과.

물론, 계약에서 언어가 지원하더라도 일부 별칭은 정의되지 않은 상태로 둘 수 있습니다.

눈에 띄는 예 :

  • 자기 할당. 이것은 아마도 비싸지 않을 것입니다. 일반적인 경우를 비판하지 마십시오.
    반면에 자기 이동 할당은 당신의 얼굴에서 폭발해서는 안되지만 아무 일도 할 필요는 없습니다.
  • 컨테이너의 요소 사본을 컨테이너에 삽입 같은 것 v.push_back(v[2]);.
  • std::copy() 대상이 소스 내부에서 시작되지 않는다고 가정합니다.

그러나 귀하의 예에는 다른 문제가 있습니다.

  • 멤버 함수는 권한을 사용하지 않으며 참조하지도 않습니다 this. 그것은 단순히 잘못된 위치에있는 무료 유틸리티 기능처럼 작동합니다.
  • 수업은 어떤 종류의 추상화도 제시하지 않습니다 . 이에 따라 내부 를 캡슐화 하거나 불변을 유지 하려고 시도하지 않습니다 . 임의의 요소로 구성된 멍청한 가방입니다.

요약하면 : 이 예는 좋지 않습니다. 그러나 멤버 함수가 멤버 객체를 전달하기 때문에 아닙니다.


0

특히이 작업을 수행하는 것은 몇 가지 좋은 이유로 좋지 않은 아이디어이지만 모든 것이 귀하의 질문에 반드시 필요한지는 확실하지 않습니다.

  • 클래스 변수 (상수가 아닌 실제 변수)를 갖는 것은 가능한 한 피해야 할 것이며 절대적으로 필요한지에 대해 진지하게 반영해야합니다.
  • 이 클래스 변수에 대한 쓰기 액세스 권한을 모든 사람에게 공개적으로 공개하는 것은 거의 보편적으로 좋지 않습니다.
  • 수업 방법은 수업과 관련이 없습니다. 실제로는 한 배열의 값을 다른 배열의 값에 추가하는 함수입니다. 이러한 종류의 함수는 함수가있는 언어의 함수 여야하며 함수가없는 언어의 "가짜 클래스"(예 : 데이터 또는 객체 동작이없는 함수 모음)의 정적 메서드 여야합니다.
  • 또는 이러한 매개 변수를 제거하여 실제 클래스 메소드로 바꾸십시오. 그러나 앞에서 언급 한 이유로 여전히 나쁩니다.
  • 왜 int 배열입니까? vector<int>당신의 인생을 더 쉽게 만들 것입니다.
  • 이 예제가 실제로 디자인을 대표하는 경우 클래스가 아닌 객체에 데이터를 배치하고 생성 후 객체 데이터의 값을 변경할 필요가없는 방식으로 이러한 객체에 대한 생성자를 구현하는 것이 좋습니다.

0

이것은 C ++에 대한 고전적인 "클래스가있는 C"접근 방식입니다. 실제로 이것은 노련한 C ++ 프로그래머가 쓰는 것이 아닙니다. 우선, 컨테이너 라이브러리를 구현하지 않는 한 원시 C 배열을 사용하는 것은 보편적으로 권장되지 않습니다.

이와 같은 것이 더 적절할 것입니다.

// Don't forget to compile with -std=c++17
#include <iostream>
using std::cout; // This style is less common, but I prefer it
using std::endl; // I'm sure it'll incite some strongly opinionated comments

#include <array>
using std::array;

#include <algorithm>

#include <vector>
using std::vector;


class MyClass {
public: // Begin lazy for brevity; don't do this
    std::array<int, 5> arr1 = {1, 2, 3, 4, 5};
    std::array<int, 5> arr2 = {10, 10, 10, 10, 10};
};

void elementwise_add_assign(
    std::array<int, 5>& lhs,
    const std::array<int, 5>& rhs
) {
    std::transform(
        lhs.begin(), lhs.end(), rhs.begin(),
        lhs.begin(),
        [](auto& l, const auto& r) -> int {
            return l + r;
        });
}

int main() {
    MyClass foo{};

    elementwise_add_assign(foo.arr1, foo.arr2);

    for(auto const& value: foo.arr1) {
        cout << value << endl; // arr1 values have been added to
    }

    for(auto const& value: foo.arr2) {
        cout << value << endl; // arr2 values remain unaltered
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.