문제

편집 :이 질문은 C ++ 자체보다 언어 공학에 관한 것입니다. 나는 C ++를 예제로 사용하여 내가 원하는 것을 보여 주었다. 주로 매일 그것을 사용하기 때문이다. 나는 그것이 C ++에서 어떻게 작동하는지 알고 싶지 않았지만 그것이 어떻게 어떻게되는지 토론을 열어 ~할 수 있었다 완료됩니다.

그것이 지금 작동하는 방식이 아닙니다. 소망 그것은 할 수 있고, 그것은 C 계산 성을 확실히 깨뜨릴 것이지만, 그것이 내가 "c"라고 생각하는 것입니다.

내 말은, 지금 선언하는 모든 기능이나 방법에서 참조 연산자를 참조 접두사로 표시하여 객체가 전송 될 것을 명시해야합니다. 나는 모든 비 POD 유형이 실제로 32 비트 이상의 모든 객체에 대해 많은 것을 사용하기 때문에 자동으로 참조로 전송되기를 바랍니다. 거의 모든 클래스입니다.

지금 당장 어떻게되는지 예시합시다. , 그리고 수업 :

class example {
    public:
        int just_use_a(const a &object);
        int use_and_mess_with_b(b &object);
        void do_nothing_on_c(c object);
};

이제 내가 원하는 것 :

class example {
    public:
        int just_use_a(const a object);
        int use_and_mess_with_b(b object);
        extern "C" void do_nothing_on_c(c object);
};

이제, do_nothing_on_c ()는 오늘날처럼 행동 할 수 있습니다.

그것은 적어도 나에게 흥미로울 것입니다. 알다 모든 비 POD 매개 변수는 참조로 제공됩니다. 실수는 명시 적으로 선언해야한다면 실수가 동일하다고 생각합니다.

C에서 오는 사람 으로부터이 변경에 대한 또 다른 관점에서, 참조 연산자는 나에게 변수를 얻는 방법으로 보인다. 주소, 그것이 제가 포인터를 얻는 데 사용한 방식입니다. 내 말은, 그것은 동일한 연산자이지만 다른 맥락에서 다른 의미 론적으로, 당신에게도 약간 잘못된 느낌이 들지 않습니까?

도움이 되었습니까?

해결책

C ++와 C ++ 시맨틱의 지점이 누락 된 것 같습니다. 당신은 사실을 놓쳤습니다 C ++는 C에서 수행 한 방식이기 때문에 모든 것을 가치별로 통과하는 데 맞습니다.. 그러나 아래에서 보여 드리겠습니다.

c

C에서는 모든 것이 가치로 전달됩니다. "Primitives"와 "Pods"는 값을 복사하여 통과합니다. 기능에서 수정하면 원본이 수정되지 않습니다. 그럼에도 불구하고 일부 포드를 복사하는 비용은 사소하지 않을 수 있습니다.

포인터 표기법 ( *)을 사용하면 참조로 통과하지 않습니다. 주소 사본을 전달합니다. 하나의 미묘한 차이와 함께 다소 동일합니다.

typedef struct { int value ; } P ;

/* p is a pointer to P */
void doSomethingElse(P * p)
{
   p->value = 32 ;
   p = malloc(sizeof(P)) ; /* Don't bother with the leak */
   p->value = 45 ;
}

void doSomething()
{
   P * p = malloc(sizeof(P)) ;
   p->value = 25 ;

   doSomethingElse(p) ;

     int i = p->value ;
   /* Value of p ? 25 ? 32 ? 42 ? */
}

p-> 값의 최종 값은 32입니다. P는 주소의 값을 복사하여 전달 되었기 때문입니다. 따라서 원래 P는 수정되지 않았습니다 (그리고 새로운 P는 유출되었습니다.

Java 및 C Sharp의 매개 변수 의미

일부는 놀랍지 만 Java에서는 모든 것이 가치로 복사됩니다. 위의 C 예는 Java에서 정확히 동일한 결과를 제공합니다. 이것은 거의 당신이 원하는 것이지만, 당신은 C에서와 같이 쉽게 참조/포인터로 "원시"를 전달할 수 없습니다.

C#에서는 "Ref"키워드를 추가했습니다. C ++의 참조와 비슷하거나 다소 작동합니다. 요점은 C#에서 함수 선언과 각 호출에 대해 언급해야한다는 것입니다. 나는 이것이 당신이 원하는 것이 아니라고 생각합니다.

C ++의 매개 변수 의미

C ++에서는 값을 복사하여 거의 모든 것이 전달됩니다. 기호의 유형 만 사용하는 경우 기호를 복사하고 있습니다 (C에서 수행됩니다). 그렇기 때문에 *를 사용할 때 기호의 주소 사본을 전달하는 이유입니다.

그러나 &를 사용할 때는 실제 객체 (구조, int, 포인터 등)를 전달한다고 가정합니다.

구문 설탕으로 착각하기 쉽습니다 (즉, 무대 뒤에서 포인터처럼 작동하며 생성 된 코드는 포인터에 동일합니다). 하지만...

진실은 기준이 구문 설탕 이상이라는 것입니다.

  • 포인터와 달리 스택에있는 것처럼 물체를 조작하는 것을 승인합니다.
  • Unline Pointers는 Const 키워드와 관련이있을 때 한 유형에서 다른 유형에서 다른 유형으로 암시 적 프로모션을 승인합니다 (주로 생성자를 통해).
  • 포인터와 달리 기호는 무효/유효하지 않아야합니다.
  • "by-copy"와 달리, 당신은 객체를 복사하는 데 쓸모없는 시간을 소비하지 않습니다.
  • "By-Copy"와 달리 [out] 매개 변수로 사용할 수 있습니다.
  • "By-Copy"와 달리 C ++에서 전체 범위의 OOP를 사용할 수 있습니다 (즉, 인터페이스를 기다리는 함수로 전체 객체를 전달합니다).

따라서 참고 문헌은 두 세계 중 최고입니다.

C 예제를 보자.

struct P { int value ; } ;

// p is a reference to a pointer to P
void doSomethingElse(P * & p)
{
   p->value = 32 ;
   p = (P *) malloc(sizeof(P)) ; // Don't bother with the leak
   p->value = 45 ;
}

void doSomething()
{
   P * p = (P *) malloc(sizeof(P)) ;
   p->value = 25 ;

   doSomethingElse(p) ;

     int i = p->value ;
   // Value of p ? 25 ? 32 ? 42 ?
}

결과는 42이고, 오래된 P가 유출되어 새 p로 대체되었습니다. C 코드와 달리 포인터의 사본이 아니라 포인터, 즉 포인터 자체에 대한 참조를 전달하기 때문입니다.

C ++로 작업 할 때 위의 예는 명확해야합니다. 그렇지 않다면, 당신은 무언가를 놓치고 있습니다.

결론

C ++는 모든 것이 작동하는 방식이기 때문에 C-Copy/Value입니다. C, C# 또는 Java (JavaScript ... : -p ...)에서는 모든 것이 작동하는 방식이기 때문입니다. C#과 마찬가지로 C ++에는 참조 연산자/키워드가 있습니다. 보너스로.

자, 내가 이해하는 한, 당신은 아마도 내가 반가락으로 부르는 일을하고있을 것입니다. C+, 즉, 제한된 C ++ 기능이있는 C입니다.

아마도 솔루션은 TypEdefs를 사용하고있을 것입니다 (그러나 C ++ 동료를 분노시켜 쓸모없는 typedefs에 의해 오염 된 코드를 볼 수 있지만,이를 수행하면 C ++가 실제로 누락 된 사실을 이해할 수 있습니다.

다른 게시물에서 말했듯이, 당신은 당신의 사고 방식을 C 개발 (무엇이든)에서 C ++ 개발로 바꿔야합니다. 그렇지 않으면 다른 언어로 이동해야합니다. 그러나 C ++ 기능으로 C 방식을 계속 프로그래밍하지 마십시오. 사용하는 관용구의 힘을 의식적으로 무시하거나 난독 화함으로써 차선책 코드를 생성하기 때문입니다.

참고 : 프리미티브 이외의 다른 것을 복사하지 마십시오. OO 용량에서 기능을 거세하며 C ++에서는 이것이 원하는 것이 아닙니다.

편집하다

질문은 약간 수정되었습니다 (참조 https://stackoverflow.com/revisions/146271/list ). 나는 나의 원래 대답을하고 아래의 새로운 질문에 대답했다.

C ++의 기본 패스-참조 시맨틱에 대해 어떻게 생각하십니까? 당신이 말했듯이, 그것은 호환성을 깨뜨릴 것이며, 프리미티브 (즉, 카피로 전달되는 내장 유형)와 스트러크/객체 (참조로 전달 될 수 있음)에 대한 다른 패스 바이가 있습니다. "통과 별"을 의미하기 위해 다른 연산자를 추가해야 할 것입니다 (extern "c"는 상당히 끔찍하고 이미 다른 것에 대해 이미 사용됩니다). 아니요, 오늘 C ++에서 수행되는 방식이 정말 마음에 듭니다.

...] 참조 연산자는 가변 주소를 얻는 방법 인 것 같습니다. 그것이 제가 포인터를 얻는 데 사용한 방식입니다. 내 말은, 그것은 동일한 연산자이지만 다른 맥락에서 다른 의미 론적으로, 당신에게도 약간 잘못된 느낌이 들지 않습니까? 예 그리고 아니오. 연산자 >> C ++ 스트림과 함께 사용될 때 시맨틱을 변경했습니다. 그런 다음 연산자 +=를 사용하여 strcat을 교체 할 수 있습니다. 나는 "포인터의 반대"로 표시되고 다른 기호를 사용하고 싶지 않았기 때문에 연산자가 사용했던 것 같아요 (ASCII는 제한되어 있으며 범위 연산자 :: 포인터 -> 다른 기호는 거의 없음을 보여줍니다. 사용 가능). 그러나 지금, If & bisers you, &&는 c ++ 0x (Super-Reference ...)에 unery &&를 추가함에 따라 당신을 정말로 불안하게 할 것입니다. 나는 아직 그것을 직접 소화하지 않았다 ...

다른 팁

코드 섹션의 의미를 완전히 변경하는 컴파일러 옵션은 나에게 정말 나쁜 생각처럼 들립니다. C ++ 구문에 사용하거나 다른 언어를 찾으십시오.

나는 오히려 모든 (비판적) 매개 변수를 참조로 만들어서 더 이상 참조를 남용하지 않을 것입니다.

C ++에 참조가 추가 된 주된 이유는 작업자 과부하를 지원합니다; "Pass-by-reference"시맨틱을 원한다면 C는 완벽하게 합리적인 방법을 가지고있었습니다 : 포인터.

포인터를 사용하면 뾰족한 객체의 값을 변경하려는 의도를 분명히하고, 함수 호출 만 살펴보면이를 볼 수 있으므로 참조를 사용하는지 확인하기 위해 함수 선언을 볼 필요가 없습니다.

또한 참조하십시오

인수를 변경하고 싶습니다. 포인터를 사용해야합니까, 아니면 참조를 사용해야합니까? 나는 강한 논리적 이유를 모른다. ````객체가 아님 ''(예 : 널 포인터)을 통과하는 경우 포인터를 사용하는 것이 합리적입니다. 내 개인적인 스타일은 객체를 수정하려는 경우 포인터를 사용하는 것입니다. 일부 컨텍스트에서는 수정이 가능하다는 것을 쉽게 발견 할 수 있기 때문입니다.

동일한 FAQ에서.

예, 나는 그것이 꽤 혼란스러운 과부하라고 생각합니다.

이것이 바로 Microsoft가 상황에 대해 말해야 할 것입니다.

참조 선언을 연산자 주소를 사용하여 혼동하지 마십시오. & 식별자가 int 또는 char와 같은 유형이 선행되면 식별자는 유형에 대한 참조로 선언됩니다. & 식별자가 유형이 우선하지 않을 때는 사용법이 연산자 주소의 사용법입니다.

나는 C 또는 C ++에서는 크지 않지만 Assembler에서 코딩하는 것보다 *와 두 언어의 다양한 용도를 분류하는 더 큰 두통을 얻습니다.

최선의 조언은 당신이 정말로 일어나고 싶은 일에 대해 생각하는 습관을 만드는 것입니다. 사본 생성자가 없거나 사용하고 싶지 않은 경우 참조로 통과하는 것이 좋습니다. 큰 물체의 경우 더 저렴합니다. 그러나 파라미터에 대한 돌연변이는 클래스 밖에서 느껴집니다. 대신 지나갈 수 있습니다 const 참조 - 돌연변이는 없지만 국소 수정을 할 수는 없습니다. 로컬 수정을 할 수있는 사본을 원할 때 함수에서 읽기 전용이어야하는 저렴한 객체에 대해 Const By-Value를 통과하십시오.

각 순열 (값/값의 회의 및 const/non-const)은 확실히 동등하지 않은 중요한 차이점이 있습니다.

가치를 통과하면 데이터를 스택에 복사하는 것입니다. 당신이 통과하는 구조물 또는 클래스에 대해 연산자 = 정의 된 경우, 실행됩니다. 제안 된 변화가 본질적으로 발생할 것이라는 암시 적 언어 혼란의 rigmarole을 씻어 낼 수 있다는 것을 알고있는 컴파일러 지침은 없습니다.

일반적인 모범 사례는 참조뿐만 아니라 Const 참조별로 값을 전달하는 것입니다. 이것은 호출 함수에서 값을 변경할 수 없도록합니다. 이것은 const-correct codebase의 한 요소입니다.

완전히 CONT-CARRECT CODEBASE가 더욱 발전하여 프로토 타입의 끝에 Const를 추가합니다. 고려하다:

void Foo::PrintStats( void ) const {
   /* Cannot modify Foo member variables */
}

void Foo::ChangeStats( void ) {
   /* Can modify foo member variables */
}

foo 객체를 Const로 접두사로 전달하려면 printstats ()를 호출 할 수 있습니다. 컴파일러는 Changestats ()에 대한 호출에 오류가 발생합니다.

void ManipulateFoo( const Foo &foo )
{
    foo.PrintStats();  // Works
    foo.ChangeStats(); // Oops; compile error
}

나는 솔직히 C ++에서 참조 아이디어로 가치를 통과/통과하는 것이 오해의 소지가 있다고 생각합니다. 모든 것 합격 가치가 있습니다. 세 가지 경우가 있습니다.

  1. 여기서 변수의 로컬 사본을 통과합니다

    void myFunct(int cantChangeMyValue)
    
  2. 파인터의 로컬 사본을 변수에 전달하는 곳

    void myFunct(int* cantChangeMyAddress) {
        *cantChangeMyAddress = 10;
    }
    
  3. 당신이 '참조'를 통과하지만 컴파일러 마법을 통해 마치 마치 포인터를 통과하고 매번 단순히 불쾌한 것처럼 보입니다.

    void myFunct(int & hereBeMagic) {
        hereBeMagic = 10; // same as 2, without the dereference
    }
    

나는 개인적으로 모든 것이 합격하다는 것을 기억하는 것이 훨씬 덜 혼란 스럽습니다. 경우에 따라 해당 값은 주소 일 수 있으며, 이는 기능 외부의 것을 변경할 수 있습니다.

당신이 제안하는 것은 프로그래머가 1 번을 할 수 없게 될 것입니다. 나는 개인적으로 그것이 그 옵션을 빼앗는 것이 나쁜 생각이라고 생각합니다. C/C ++의 주요 장점 중 하나는 메모리 관리가 잘 알려진 것입니다. 모든 것을 참조별로 통과시키는 것은 단순히 C ++를 Java처럼 만들려고 노력하는 것입니다.

명확하지 않은 것이 있습니다. 당신이 말할 때:

int b (b ¶m);

두 번째 'B'를 위해 무엇을 의도 했습니까? 유형을 소개하는 것을 잊었습니까? 첫 번째 'B'와 관련하여 다르게 쓰는 것을 잊었습니까? 다음과 같은 글을 쓰는 것이 분명하다고 생각하지 마십시오.

class B{/*something...*/};
int b(B& param);

지금부터, 나는 당신이 내가 쓴 것을 의미한다고 생각합니다.

이제 귀하의 질문은 "컴파일러가 비 POD의 모든 통과 가치를 패스별로 고려할 것이라는 것이 더 좋을 것이라고 생각하지 않습니까?"입니다. 첫 번째 문제는 계약이 파괴된다는 것입니다. 나는 당신이 참조만으로도 통과 할 수있는 회의를 의미한다고 생각합니다.

이제 귀하의 질문이 이것으로 줄어 듭니다. "값으로 기능 호출을 최적화 할 수있는 일부 컴파일러 지침이 있는지 알고 있습니까?"

지금 대답은 "모르겠다"입니다.

가능한 모든 종류의 매개 변수를 Const 변형과 혼합하기 시작하면 C ++가 매우 지저분 해진다고 생각합니다.

그것은 모든 사본 생성자 호출을 trak으로 빠르게 손을 떼고, 모든 부정 수가 과부하 된 등을 켜야합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top