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


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.