C #은 C ++보다 "자신을 매달 수있는 줄이 적습니까"를 제공합니까? [닫은]


14

Joel Spolsky는 C ++에 "충분히 줄을 걸기에 충분"하다고 특징 지었습니다 . 실제로 그는 Scott Meyers의 "Effective C ++"을 요약했습니다.

이 책은 기본적으로 C ++은 자신을 걸기에 충분한 밧줄이며 그다음 몇 마일을 더 걸고 M & M으로 위장한 자살 약 두 개를 말합니다.

책의 사본이 없지만 책의 많은 부분이 런타임에서 이러한 문제를 관리하기 때문에 C #에서 무질서하게 렌더링되는 것처럼 보이는 메모리 관리의 함정 과 관련이 있다는 표시가 있습니다.

내 질문은 다음과 같습니다.

  1. C #은 신중한 프로그래밍으로 만 C ++에서 피할 수있는 함정을 피합니까? 그렇다면 어느 정도, 어떻게 피할 수 있습니까?
  2. 새로운 C # 프로그래머가 알고 있어야하는 C #에 새로운 함정이 있습니까? 그렇다면 왜 C # 디자인으로 피할 수 없었습니까?

10
로부터 자주 묻는 질문 : Your questions should be reasonably scoped. If you can imagine an entire book that answers your question, you’re asking too much.. 나는 이것이 그러한 질문으로 자격이 있다고 믿는다.
Oded

@Oded 문자 제한 헤드 라인 질문을 참조하고 있습니까? 아니면 내 게시물 본문에 3 + 더 정확한 질문이 있습니까?
alx9r

3
솔직히 - 제목과 모두 각각 은 "더 정확한 질문"의.
Oded

3
이 질문에 대한 메타 토론 을 시작했습니다 .
Oded

1
삭제 된 세 번째 질문과 관련하여 Bill Wagner의 Effective C # 시리즈 (현재 3 권의 책) 는 주제에 관해 읽은 것보다 C # 프로그래밍에 대해 더 많이 가르쳐주었습니다. EC #에 대한 Martins의 검토는 Effective C ++를 직접 대체 할 수 없다는 점에서 옳지 만, 그렇게해야한다고 잘못 생각하고 있습니다. 더 이상 쉬운 실수 에 대해 걱정할 필요가 없으면 더 어려운 실수로 넘어 가야합니다.
마크 부스

답변:


33

C ++과 C # 의 근본적인 차이점은 정의되지 않은 동작 에서 비롯됩니다 .

수동 메모리 관리와 는 아무런 관련없습니다 . 두 경우 모두 해결 된 문제입니다.

C / C ++ :

C ++에서 실수를하면 결과가 정의되지 않습니다.
또는 시스템에 대해 특정 종류의 가정을 시도하면 (예 : 부호있는 정수 오버플로) 프로그램이 정의되지 않을 가능성이 있습니다.

어쩌면 이 3 부분 시리즈 읽을 정의되지 않은 행동을.

이것이 C ++를 매우 빠르게 만드는 것입니다. 컴파일러는 문제가 발생할 때 발생하는 일에 대해 걱정할 필요가 없으므로 정확성 검사를 피할 수 있습니다.

C #, Java 등

C #에서, 당신은하고 보장 많은 실수는 예외로 당신의 얼굴에 날려 버릴 것입니다, 당신은 보장하고 더 많은 기본 시스템에 대한.
이는 C #을 C ++만큼 빠르게 만드는 데있어 근본적인 장벽이지만 C ++를 안전하게 만드는 데에도 기본적인 장벽이며 C #을보다 쉽게 ​​작업하고 디버깅 할 수 있습니다.

다른 모든 것은 단지 국물입니다.


정의되지 않은 모든 항목은 실제로 구현에 의해 정의되므로 Visual Studio에서 부호없는 정수를 오버플로하면 올바른 컴파일러 플래그를 설정하면 예외가 발생합니다. 이제 나는 이것이 당신이 말하는 것에 대해 알고 있지만, 정의 되지 않은 행동 은 아니며 사람들이 일반적으로 확인하지 않는 것입니다. (operator ++와 같이 정의되지 않은 동작과 동일하며 각 컴파일러에 의해 잘 정의되어 있습니다). C #과 동일하게 말할 수는 있지만 단 하나의 구현 만 있습니다-Mono에서 실행할 경우 많은 '정의되지 않은 행동'이 있습니다. bugzilla.xamarin.com/show_bug.cgi?id=310
gbjbaanb

1
실제로 현재 버전의 Windows에서 현재 .net 구현으로 정의되거나 정의되어 있습니까? g ++ 이하 는대로 정의하면 C ++ 정의되지 않은 동작조차 완전히 정의됩니다.
Martin Beckett

6
부호없는 정수 오버플로는 전혀 UB가 아닙니다. UB 인 부호있는 정수가 넘 칩니다.
DeadMG

6
@gbjbaanb : DeadMG가 말했듯이-부호있는 정수 오버플로 정의되지 않았습니다. 구현 정의 가 아닙니다 . 이 문구들은 C ++ 표준에서 특정한 의미를 가지고 있으며 같은 것이 아닙니다. 실수하지 마십시오.
user541686

1
@CharlesSalvia : 어, C #보다 "C ++를 사용하면 CPU 캐시를보다 쉽게 ​​활용할 수있는 방법"이 정확히 무엇입니까? 그리고 C ++은 C #에서 가질 수없는 메모리에 대해 어떤 종류의 제어를 제공합니까?
user541686

12

C #은 신중한 프로그래밍으로 만 C ++에서 피할 수있는 함정을 피합니까? 그렇다면 어느 정도, 어떻게 피할 수 있습니까?

대부분 그렇지 않습니다. 물론 새로운 것을 만듭니다.

  1. 정의되지 않은 동작 -C ++의 가장 큰 함정은 정의되지 않은 언어가 많이 있다는 것입니다. 컴파일러는 이러한 일을 할 때 문자 그대로 우주를 날려 버릴 수 있으며 괜찮을 것입니다. 당연히, 이것은 드문 일이지만, 그것은 이다 하나 개의 시스템에 정말 이유없이 다른에없는 일을 위해 잘 작동에 프로그램에 대한 매우 일반적인. 또는 더 미묘하게 다르게 행동하십시오. C #에는 사양에 정의되지 않은 동작이 몇 가지 있지만 드물고 자주 사용되지 않는 언어 영역에서는 발생합니다. C ++은 명령문을 작성할 때마다 정의되지 않은 동작이 발생할 수 있습니다.

  2. 메모리 누수 -이것은 현대 C ++에 대한 걱정은 아니지만 초보자와 수명의 절반 정도 동안 C ++을 사용하면 메모리 누출이 매우 쉽습니다. 효과적인 C ++은 이러한 문제를 해결하기 위해 실용화의 발전에 따라 나왔습니다. 즉, C # 여전히 메모리를 누출 시킬 수 있습니다. 사람들이 가장 많이 겪는 사례는 이벤트 캡처입니다. 객체가 있고 그 메소드 중 하나를 이벤트 핸들러로 이벤트에 넣을 경우, 해당 이벤트의 소유자는 객체가 죽도록 GC해야합니다. 대부분의 초보자는 이벤트 핸들러가 참조로 계산된다는 것을 인식하지 못합니다. 또한 메모리를 누수 할 수있는 일회용 리소스를 폐기하지 않는 문제도 있지만, 효과적인 C ++의 포인터만큼 일반적이지 않습니다.

  3. 컴파일 -C ++에는 지연된 컴파일 모델이 있습니다. 이것은 많은 트릭으로 잘 플레이하고 컴파일 시간을 줄입니다.

  4. Strings -Modern C ++는 이것을 조금 개선하지만 char*2000 년 이전의 모든 보안 침해의 ~ 95 %를 담당합니다. 숙련 된 프로그래머에게는 초점을 맞출 std::string것이지만 여전히 피해야 할 문제입니다. . 그리고 유니 코드 지원이 필요하지 않기를기도합니다.

그리고 그것은 빙산의 일각입니다. 가장 큰 문제는 C ++가 초보자에게는 매우 열악한 언어입니다. 그것은 상당히 일관성 이 없으며 , 구식의 정말 나쁜 나쁜 함정은 관용구를 바꿔서 다루어졌습니다. 문제는 초보자가 Effective C ++와 같은 관용구를 익혀야한다는 것입니다. C #은 이러한 많은 문제를 완전히 제거하고 학습 과정을 진행할 때까지 나머지 문제를 덜 걱정합니다.

새로운 C # 프로그래머가 알고 있어야하는 C #에 새로운 함정이 있습니까? 그렇다면 C # 디자인으로 피할 수 없었던 이유는 무엇입니까?

이벤트 "메모리 누수"문제에 대해 언급했습니다. 프로그래머가 언어로 할 수없는 것을 기대하는 것만 큼 언어 문제는 아닙니다.

다른 하나는 C # 개체의 종료자가 기술적 으로 런타임에 의해 실행되도록 보장 되지 않는다는 것 입니다. 이것은 일반적으로 중요 하지 않지만 일부 항목은 예상과 다르게 설계됩니다.

프로그래머가 본 또 다른 반 함정은 익명 함수의 캡처 의미입니다. 변수를 캡처하면 변수가 캡처 됩니다. 예:

List<Action> actions = new List<Action>();
for(int x = 0; x < 10; ++x ){
    actions.Add(() => Console.WriteLine(x));
}

foreach(var action in actions){
    action();
}

순진하게 생각되는 것을하지 않습니다. 1010 회 인쇄 됩니다.

잊어 버린 다른 사람들이 많이 있지만, 주된 문제는 덜 널리 퍼져 있다는 것입니다.


4
메모리 누수는 과거의 일이며, 그렇습니다 char*. C #에서 메모리를 여전히 누출 할 수 있다는 것은 말할 것도 없습니다.
DeadMG

2
"글로리 된 문자열 붙여 넣기"템플릿을 호출하는 것은 약간입니다. 템플릿은 실제로 C ++의 최고의 기능 중 하나입니다.
Charles Salvia

2
@CharlesSalvia 물론, 그들은이다 C ++의 정말 큰 특징. 그리고 그렇습니다. 이것은 컴파일 효과에 대한 지나치게 단순화 된 것일 수 있습니다. 그러나 특히 조심하지 않으면 컴파일 시간과 출력 크기에 불균형하게 영향을 미칩니다.
Telastyn

2
@deadMG는 확실히 C ++에서 사용 / 필요한 많은 템플릿 메타 프로그래밍 트릭이 다른 메커니즘을 통해 더 잘 구현된다고 주장합니다.
Telastyn

2
@Telastyn type_traits의 요점은 컴파일 타임에 타입 정보를 얻는 것이므로이 정보를 사용하여 특정 방식으로 특수한 템플릿이나 과부하 함수를 사용하여 특정 방식으로 기능을 수행 할 수 있습니다.enable_if
Charles Salvia

10

제 생각에는 C ++의 위험이 다소 과장되어 있습니다.

C #에서는 unsafe키워드를 사용하여 "안전하지 않은"포인터 작업을 수행 할 수 있지만 C ++ (대부분 C의 상위 집합 임)를 사용하면 필요할 때마다 포인터를 사용할 수 있습니다. 메모리 누수, 버퍼 오버플로, 댕글 링 포인터 등과 같은 포인터 (C와 동일)를 사용하는 데 따르는 일반적인 위험 외에도 C ++은 물건을 심각하게 망치는 새로운 방법을 소개합니다.

Joel Spolsky가 말한 이 "추가 로프" 기본적으로 한 가지로 귀결 됩니다. 내부적으로 자체 메모리를 관리하는 클래스 작성, " 3의 규칙 "(현재는 규칙이라고도 함) C ++ 11에서 4 또는 규칙 5). 즉, 자체 메모리 할당을 내부적으로 관리하는 클래스를 작성하려면 수행중인 작업을 알아야합니다. 그렇지 않으면 프로그램이 중단 될 수 있습니다. 생성자, 복사 생성자, 소멸자 및 대입 연산자를 신중하게 작성해야합니다. 이는 놀랍게도 잘못되기 쉬우 며, 종종 런타임에 이상한 충돌을 일으 킵니다.

그러나 실제로 매일의 C ++ 프로그래밍에서 자체 메모리를 관리하는 클래스를 작성하는 것은 매우 드물기 때문에 C ++ 프로그래머가 이러한 함정을 피하기 위해 항상 "주의"해야한다고 오도하는 것은 잘못입니다. 일반적으로 다음과 같은 작업을 수행하게됩니다.

class Foo
{
    public:

    Foo(const std::string& s) 
        : m_first_name(s)
    { }

    private:

    std::string m_first_name;
};

이 클래스는 Java 또는 C #에서 수행하는 작업과 거의 비슷해 보입니다. 라이브러리 클래스 std::string는 자동으로 처리 하므로 명시 적 메모리 관리가 필요하지 않으며 기본값 이후 "Rule of 3"항목이 필요하지 않습니다. 복사 생성자와 할당 연산자는 괜찮습니다.

다음과 같은 일을하려고 할 때만 가능합니다.

class Foo
{
    public:

    Foo(const char* s)
    { 
        std::size_t len = std::strlen(s);
        m_name = new char[len + 1];
        std::strcpy(m_name, s);
    }

    Foo(const Foo& f); // must implement proper copy constructor

    Foo& operator = (const Foo& f); // must implement proper assignment operator

    ~Foo(); // must free resource in destructor

    private:

    char* m_name;
};

이 경우 초보자는 할당, 소멸자 및 복사 생성자를 올바르게 얻는 것이 까다로울 수 있습니다. 그러나 대부분의 경우이 작업을 수행 할 이유가 없습니다. C ++를 사용하면 std::stringand 같은 라이브러리 클래스를 사용하여 수동 메모리 관리 시간을 99 % 피할 수 있습니다 std::vector.

또 다른 관련 문제는 예외가 발생할 가능성을 고려하지 않는 방식으로 메모리를 수동으로 관리하는 것입니다. 처럼:

char* s = new char[100];
some_function_which_may_throw();
/* ... */
delete[] s;

경우 some_function_which_may_throw()실제로 않는 예외를 throw에 할당 된 메모리가 있기 때문에, 당신은 메모리 누수로 남겨 s재생되지 않습니다. 그러나 다시 말하지만 실제로는 "3 규칙"이 더 이상 큰 문제가되지 않는 것과 같은 이유로 더 이상 문제가되지 않습니다. 원시 포인터로 실제로 자신의 메모리를 관리하는 것은 매우 드 (니다 (보통 불필요합니다). 위의 문제를 방지하려면, 당신이해야 할 것 모두가 사용할 수 있습니다 std::string또는 std::vector, 그리고 소멸자가 자동으로 예외가 발생 된 후 스택 언 와인딩 중에 호출받을 것입니다.

따라서 일반적인 주제는 자동 초기화 / 파괴, 복사 생성자 및 예외와 같이 C에서 상속 되지 않은 많은 C ++ 기능이 C ++ 에서 수동 메모리 관리를 수행 할 때 프로그래머가주의해야한다는 것입니다. 그러나 다시 말하지만, 처음에는 수동 메모리 관리를 수행하려는 경우에만 문제가됩니다. 표준 컨테이너와 스마트 포인터가있을 때는 더 이상 필요하지 않습니다.

따라서 C ++은 많은 추가 로프를 제공하지만 로프를 사용하여 매달릴 필요는 없으며 Joel이 말한 함정은 현대 C ++에서 피하기 쉽습니다.


C ++ 03에서는 3의 규칙이었고 C ++ 11에서는 4의 규칙이되었습니다.
DeadMG

1
복사 생성자, 이동 생성자, 복사 할당, 이동 할당 및 소멸자를 "Rule of 5"라고 부를 수 있습니다. 그러나 적절한 자원 관리를 위해 이동 의미가 항상 필요한 것은 아닙니다.
Charles Salvia

별도의 이동 및 복사 할당이 필요하지 않습니다. 복사 및 스왑 숙어는 두 연산자를 한 번에 수행 할 수 있습니다.
DeadMG

2
질문에 대답합니다 Does C# avoid pitfalls that are avoided in C++ only by careful programming?. "조엘이 현대 C ++에서 이야기했던 함정을 피하기가 쉽지 않기 때문에"실제로 대답은 아닙니다.
Charles Salvia

1
IMO는 C # 또는 Java와 같은 고급 언어가 메모리 관리 및 기타 유용한 기능을 제공 하지만 항상 예상대로 작동하지는 않습니다. 코드 디자인을 계속 살펴보면 메모리 누출 이 발생하지 않습니다 (C ++에서 정확히 부르는 것이 아닙니다). 필자의 경험에 따르면 소멸자가 호출되고 대부분의 경우 정리를 수행한다는 것을 알고 있기 때문에 C ++에서 메모리를 관리하는 것이 더 쉽다는 것을 알았습니다. 결국 C ++에는 효율적인 메모리 관리가 불가능한 디자인에 대한 스마트 포인터가 있습니다. C ++은 훌륭하지만 인형에는 적합하지 않습니다.
Pijusn

3

나는 정말로 동의하지 않을 것입니다. 1985 년에 존재했던 C ++보다 함정이 적을 수도 있습니다.

C #은 신중한 프로그래밍으로 만 C ++에서 피할 수있는 함정을 피합니까? 그렇다면 어느 정도, 어떻게 피할 수 있습니까?

실제로는 아닙니다. 세 가지의 규칙과 같은 규칙에 C ++ (11 개) 덕분에 엄청난 의미를 상실 unique_ptr하고 shared_ptr표준화 된 서비스를 제공합니다. 모호한 방식으로 표준 클래스를 사용하는 것은 "주의 코딩"이 아니라 "기본 코딩"입니다. 또한 수동 메모리 관리와 같은 작업을 수행하기에 여전히 어리 석거나 정보가 없거나 둘 다인 C ++ 인구의 비율은 이전보다 훨씬 낮습니다. 현실적으로 표준 클래스는 상상할 수있는 거의 모든 유스 케이스를 다루기 때문에 규칙을 보여주고 싶은 강사는 여전히 적용되는 예제를 찾기 위해 몇 주를 소비해야합니다. 많은 효과적인 C ++ 기술은 dodo와 같은 방식으로 진행되었습니다. 다른 많은 것들이 실제로 C ++에만 국한된 것은 아닙니다. 보자 첫 번째 항목을 건너 뛰고 다음 10 개는 다음과 같습니다.

  1. C ++처럼 C ++를 코딩하지 마십시오. 이것은 실제로 상식입니다.
  2. 인터페이스를 제한하고 캡슐화를 사용하십시오. 죄송합니다.
  3. 2 단계 초기화 코드 작성자는 스테이크에서 태워야합니다. 죄송합니다.
  4. 가치 의미가 무엇인지 알 수 있습니다. 이것은 실제로 C ++에만 해당됩니까?
  5. 이번에는 약간 다른 방식으로 인터페이스를 다시 제한하십시오. 죄송합니다.
  6. 가상 소멸자. 네. 이것은 아마도 여전히 유효합니다-다소. final그리고 override변화에게 더 나은이 특정 게임을 도왔다. 소멸자를 만드십시오. 소멸자 override를 만들지 않은 사람으로부터 상속받은 경우 멋진 컴파일러 오류를 보장합니다 virtual. final가상 소멸자없이 수업 을 진행하면 나쁜 스크럽을 피할 수 있습니다.
  7. 정리 기능이 실패하면 나쁜 일이 발생합니다. 이것은 실제로 C ++에만 국한된 것은 아닙니다. Java와 C # 모두에 대해 동일한 조언을 볼 수 있습니다. 실패 할 수있는 정리 함수를 갖는 것은 매우 나쁜 일이며이 항목에 대한 C ++ 또는 OOP는 없습니다.
  8. 생성자 순서가 가상 함수에 미치는 영향을 알고 있어야합니다. 유감스럽게도 Java (현재 또는 과거)에서는 파생 클래스의 함수를 잘못 호출하기 때문에 C ++의 동작보다 훨씬 나쁩니다. 어쨌든이 문제는 C ++에만 국한되지 않습니다.
  9. 작업자 과부하는 사람들이 예상 한대로 작동해야합니다. 실제로 구체적이지 않습니다. 지옥, 그것은 심지어 특정 연산자 오버로드조차 거의 없으며, 동일한 기능을 모든 함수에 적용 할 수 있습니다. 이름을 지정하지 않고 완전히 직관적이지 않은 작업을 수행하십시오.
  10. 이것은 실제로 나쁜 습관으로 간주됩니다. 예외적으로 안전한 모든 할당 연산자는 자체 할당을 잘 처리하며 자체 할당은 사실상 논리적 프로그램 오류이며 자체 할당을 확인하는 것은 성능 비용이 들지 않습니다.

분명히 모든 효과적인 C ++ 항목을 다룰 것은 아니지만 대부분은 C ++에 기본 개념을 적용하고 있습니다. 값 형식의 객체 지향 과부하 연산자 언어에서 동일한 조언을 찾을 수 있습니다. 가상 소멸자는 C ++ 함정에 불과하며 여전히 유효합니다.하지만 finalC ++ 11 클래스에서는 그다지 유효하지 않습니다. 효과적인 C ++는 OOP를 적용하려는 아이디어와 C ++의 특정 기능이 여전히 매우 새롭다는 점을 명심하십시오. 이 항목들은 C ++의 함정에 대해서는 거의 없으며 C의 변화에 ​​대처하는 방법과 OOP를 올바르게 사용하는 방법에 대한 것입니다.

편집 : C ++의 함정에는 함정과 같은 것들이 포함되어 있지 않습니다 malloc. 즉, C 코드에서 찾을 수있는 모든 단일 함정은 안전하지 않은 C # 코드에서 동일하게 찾을 수 있으므로 특별히 관련이 없으며 두 번째로 표준이 상호 운용을 위해 그것을 정의한다고해서 사용이 C ++로 간주된다는 의미는 아닙니다. 암호. 표준도 정의 goto되어 있지만, 그것을 사용하여 거대한 스파게티 엉망 더미를 작성한다면 언어의 문제가 아니라고 생각합니다. "주의 깊은 코딩"과 "언어의 기본 관용구를 따르는 것"에는 큰 차이가 있습니다.

새로운 C # 프로그래머가 알고 있어야하는 C #에 새로운 함정이 있습니까? 그렇다면 C # 디자인으로 피할 수 없었던 이유는 무엇입니까?

using짜증나 정말 그렇습니다. 그리고 왜 더 좋은 일이 이루어지지 않았는지 전혀 모른다. 또한 Base[] = Derived[]오리지널 디자이너가 템플릿이 C ++로 된 엄청난 성공을 인식하지 못하고 "모든 것이 모든 것을 상속받으며 모든 유형의 안전을 잃어 버리자"는 것이 현명한 선택이기 때문에 존재하는 거의 모든 Object 사용 . 또한 대의원과의 경쟁 조건 및 기타 재미와 같은 일에서 불쾌한 놀라움을 발견 할 수 있다고 생각합니다. 그런 다음 템플릿과 비교할 때 제네릭이 끔찍하게 빨리는 방식, 실제로는 실제로 불필요하게 강제로 배치하는 등의 다른 일반적인 class것들이 있습니다.


5
교육받은 사용자층이나 새로운 구성물이 실제로 줄어드는 것은 아닙니다. 그들은 단지 해결 방법이므로 사람들이 줄어 들지 않습니다. 이것은 효과적인 C ++ 및 언어의 진화에 대한 컨텍스트에 대한 모든 좋은 논평입니다.
Telastyn

2
아니요. Effective C ++의 여러 항목이 어떤 값 유형의 객체 지향 언어에 동일하게 적용될 수있는 개념인지에 관한 것입니다. 그리고 C 대신 실제 C ++를 코딩하도록 사용자 기반을 교육하는 것은 C ++이 제공하는 로프를 확실히 감소시킵니다. 또한 새로운 언어 구조 줄어드는 것을 기대합니다 . 그것은 C ++ 표준이 정의했기 때문에 malloc그렇게해야한다는 것을 의미하지는 않습니다. 계절 goto처럼 창녀가 될 수 있다는 것 이상으로 당신이 매달릴 수있는 밧줄이라는 것을 의미합니다.
DeadMG

2
C ++의 C 부분을 사용하는 unsafe것은 C #으로 모든 코드를 작성하는 것과 다르지 않습니다. 원하는 경우 C와 같은 C # 코딩의 모든 함정을 나열 할 수 있습니다.
DeadMG

@DeadMG : 그래서 정말로 문제는 "C ++ 프로그래머는 C 프로그래머 인 한 자신을 걸기에 충분한 밧줄이 있습니다"
gbjbaanb

또한 수동 메모리 관리와 같은 작업을 수행하기에 충분히 어리 석거나 정보가 없거나 C ++ 인구의 비율은 이전보다 훨씬 낮습니다. " 인용이 필요했습니다.
dan04

3

C #은 신중한 프로그래밍으로 만 C ++에서 피할 수있는 함정을 피합니까? 그렇다면 어느 정도, 어떻게 피할 수 있습니까?

C #의 장점은 다음과 같습니다.

  • C와 역 호환되지 않으므로 구문 상 편리하지만 현재 나쁜 스타일로 간주되는 "악한"언어 기능 (예 : 원시 포인터)의 목록이 길지 않습니다.
  • 효과적인 C ++ 항목 중 10 개 이상을 약화시키는 새로운 의미 함정을 의미하는 값 의미론 대신 참조 의미론을 가짐 .
  • C ++보다 구현 정의 동작이 적습니다.
    • 특히 C ++ char에서는 string, 등 의 문자 인코딩 이 구현 정의되어 있습니다. Windows 유니 코드 접근 방식 ( wchar_tUTF-16의 char경우 더 이상 사용되지 않는 "코드 페이지")과 * nix 접근 방식 (UTF-8) 사이의 스키마는 크로스 플랫폼 코드에서 큰 어려움을 초래합니다. C #, OTOH는 a string가 UTF-16 임을 보증합니다 .

새로운 C # 프로그래머가 알고 있어야하는 C #에 새로운 함정이 있습니까?

예: IDisposable

C #에 대해 "Effective C ++"에 해당하는 책이 있습니까?

라는 책있어 효과적인 C #을 구조가 유사하다 ++ 효과적인 C는 .


0

아니요, C # (및 Java)은 C ++보다 안전하지 않습니다.

C ++은 로컬에서 확인할 수 있습니다. C ++에서 단일 클래스를 검사하고 참조 된 모든 클래스가 올바른 것으로 가정하여 클래스가 메모리 또는 기타 리소스를 누출하지 않는지 확인할 수 있습니다. Java 또는 C #에서는 모든 참조 클래스를 확인하여 어떤 종류의 마무리가 필요한지 판별해야합니다.

C ++ :

{
   some_resource r(...);  // resource initialized
   ...
}  // resource destructor called, no leaks here

씨#:

{
   SomeResource r = new SomeResource(...); // resource initialized
   ...
} // did I need to finalize that?  May I should have used 'using' 
  // (or in Java, a grotesque try/finally construct)?  No way to tell
  // without checking the documentation for SomeResource

C ++ :

{
    auto_ptr<SomeInterface> i = SomeFactory.create(...);
    i->f(...);
} // automatic finalization and memory release.  A new implementation of
  // SomeInterface can allocate and free resources with no impact
  // on existing code

씨#:

{
   SomeInterface i = SomeFactory.create(...);
   i.f(...);
   ...
} // Sure hope someone didn't create an implementation of SomeInterface
  // that requires finalization.  In C# and Java it is necessary to decide whether
  // any implementation could require finalization when the interface is defined.
  // If the initial decision is 'no finalization', then no future implementation  
  // can acquire any resource without creating potential leaks in existing code.

3
... IDSposable에서 무언가를 상속 받는지를 결정하는 것은 현대 IDE에서 매우 사소합니다. 주요 문제는 사용 (또는 그와 비슷한 몇 가지) 을 알아야 한다는 것 auto_ptr입니다. 그것이 속담입니다.
Telastyn

2
@Telastyn 아니오, 요점은 실제로 필요하지 않다는 것을 알지 않는 한 항상 스마트 포인터를 사용한다는 것입니다. C #에서 using 문은 참조하는 로프와 같습니다. (C ++에서는 똑똑한 포인터를 사용해야한다는 것을 기억해야한다. 왜 항상 using 문을 사용해야한다는 것을 기억해야하는데 C #이 나쁘지는
않은가

1
@gbjbaanb 무엇 때문에? C # 클래스의 최대 5 %가 일회용입니까? 그리고 당신은 알고 당신은 그들이 일회용 인 경우를 폐기해야합니다. C ++에서 모든 단일 객체는 일회용입니다. 특정 인스턴스를 처리 해야하는지 알 수 없습니다 . 팩토리가 아닌 포인터가 반환되면 어떻게됩니까? 청소하는 것이 당신의 책임입니까? 그것은 될 수 있지만 때로는이다. 다시 말하지만, 항상 스마트 포인터를 사용해야한다고해서 존재하지 않는 옵션이 있다는 의미는 아닙니다. 특히 초보자에게는 이것이 중요한 함정입니다.
Telastyn

2
@Telastyn : 사용 auto_ptr을 아는 것은 IEnumerable인터페이스 를 사용 하거나 사용하는 것을 아는 것만 큼 간단 하거나 통화 등에 부동 소수점을 사용하지 않습니다. DRY의 기본 응용 프로그램입니다. 프로그래밍 방법의 기본 사항을 아는 사람은 그러한 실수를하지 않을 것입니다. 달리 using. 문제 using모든 클래스에 대해 일회용인지 여부 를 알고 있어야 하며 , 절대 변경되지 않기를 희망하며, 일회용이 아닌 경우 일회용이어야하는 파생 클래스를 모두 자동으로 금지한다는 것입니다.
DeadMG

2
케빈 : 어, 당신의 대답은 말이되지 않습니다. 당신이 잘못하고있는 것은 C #의 잘못이 아닙니다. 당신은 NOT 제대로 작성된 C # 코드의 파이 나라에 따라 달라집니다 . Dispose메소드 가있는 필드가 있으면 구현해야합니다 IDisposable( '적절한'방법). 클래스가 그렇게하면 (C ++에서 클래스에 RAII를 구현하는 것과 동일) C ++ using의 스마트 포인터와 같은 것을 사용 하면 모두 완벽하게 작동합니다. 파이널 라이저는 주로 사고를 예방하기 Dispose위한 것입니다. 정확성에 대한 책임이 있으며,이를 사용하지 않으면 C #이 아니라 내 잘못입니다.
user541686

0

예 100 % 예 메모리를 확보하고 C #에서 메모리를 사용하는 것이 불가능하다고 생각합니다 (관리되는 것으로 가정하고 안전하지 않은 모드로 전환하지 않는다고 가정).

그러나 C ++로 프로그래밍하는 방법을 알고 있다면 믿을 수없는 사람들은 많지 않습니다. 당신은 꽤 괜찮아요. Charles Salvia 클래스와 마찬가지로 기존 STL 클래스에서 모두 처리되므로 메모리를 실제로 관리하지 않습니다. 포인터를 거의 사용하지 않습니다. 사실 나는 단일 포인터를 사용하지 않고 프로젝트를 갔다. (C ++ 11은 이것을 쉽게 만듭니다).

오타, 어리석은 실수 등을 만드는 경우 (예 : if (i=0)bc == 정말 빨리 치면 키가 멈췄습니다) 컴파일러는 코드의 품질을 향상 시키므로 좋은 불평을합니다. 다른 예제는 breakswitch 문을 잊어 버리고 함수에서 정적 변수를 선언 할 수 없도록합니다 (때로는 싫어하지만 좋은 아이디어입니다).


4
Java와 C #은 참조 평등을 사용하고 값 평등을 도입 하여 =/ ==문제를 더욱 악화 시켰습니다 . 가난한 프로그래머는 이제 변수가 'double'인지 'Double'인지 추적하고 올바른 변형을 호출해야합니다. ==.equals
케빈 클라인

@ kevincline +1이지만 C # 에서는 대부분 문자열, 정수 및 부동 소수점 (즉, 구조체 멤버 만)을 갖기 때문에 엄청나게 잘 작동 struct할 수 있습니다 ==. 내 자신의 코드에서는 배열을 비교할 때를 제외하고는 문제가 발생하지 않습니다. 나는 목록 또는 비 구조체 유형 (string, int, float, DateTime, KeyValuePair 및 기타 여러 유형)을 비교하지 않는다고 생각합니다.

2
파이썬은 ==가치 평등과 is참조 평등 을 위해 그것을 사용했습니다 .
dan04

@ dan04-C #에는 몇 가지 유형의 평등이 있다고 생각하십니까? 우수한 ACCU 번개 이야기를 참조하십시오 : 일부 개체가 다른 사람보다 더 평등하다
마크 부스
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.