C ++ 참조 변수를 반환하는 관행이 사악합니까? 따르면 , 참조

이것은 내가 생각하는 약간 주관적입니다. 의견이 만장일치인지 확실하지 않습니다 (참조가 반환되는 많은 코드 스 니펫을 보았습니다).

이 질문 에 대한 의견에 따르면 , 참조 초기화와 관련하여 방금 요청한 참조를 삭제하는 것이 더 쉽게 누락되어 메모리 누수가 발생할 수 있으므로 참조 를 반환하는 것은 악의적 일 수 있습니다.

나는 내가 예제를 따르고 (내가 상상하지 않는 한) 공정한 몇 곳에서 이것을 했으므로 걱정이됩니다 … 오해 했습니까? 사악한가요? 그렇다면 얼마나 악한가?

필자는 C ++을 처음 접했다는 사실과 결합 된 포인터와 참조가 혼합되어 있기 때문에 언제 사용해야 할 지에 대한 혼란이 내 응용 프로그램에서 메모리 누수 문제라고 생각합니다.

또한 스마트 / 공유 포인터 사용은 일반적으로 메모리 누수를 피하는 가장 좋은 방법으로 받아 들여진다는 것을 알고 있습니다.



답변

일반적으로 참조 반환은 완벽하게 정상이며 항상 발생합니다.

당신이 의미하는 경우 :

int& getInt() {
    int i;
    return i;  // DON'T DO THIS.
}

그것은 모든 종류의 악입니다. 스택 할당 i이 사라지고 아무것도 언급하지 않습니다. 이것은 또한 악하다 :

int& getInt() {
    int* i = new int;
    return *i;  // DON'T DO THIS.
}

이제 클라이언트는 결국 이상한 일을해야하기 때문에 :

int& myInt = getInt(); // note the &, we cannot lose this reference!
delete &myInt;         // must delete...totally weird and  evil

int oops = getInt(); 
delete &oops; // undefined behavior, we're wrongly deleting a copy, not the original

rvalue 참조는 여전히 참조 일 뿐이므로 모든 악의적 인 응용 프로그램은 동일하게 유지됩니다.

함수의 범위를 벗어나는 것을 할당하려면 스마트 포인터 (또는 일반적으로 컨테이너)를 사용하십시오.

std::unique_ptr<int> getInt() {
    return std::make_unique<int>(0);
}

이제 클라이언트는 스마트 포인터를 저장합니다.

std::unique_ptr<int> x = getInt();

다음과 같이 수명이 더 높은 수준으로 공개되어있는 것을 액세스하는 경우에도 참조가 가능합니다.

struct immutableint {
    immutableint(int i) : i_(i) {}

    const int& get() const { return i_; }
private:
    int i_;
};

여기서 우리 i_가 호출하는 것이 클래스 인스턴스의 수명을 관리하므로 i_적어도 그 수명을 유지 하기 때문에 참조를 반환해도 괜찮습니다 .

그리고 물론, 단지 아무 문제가 없습니다 :

int getInt() {
   return 0;
}

평생을 발신자에게 맡겨야하고 가치를 계산하는 것입니다.

요약 : 호출 후 객체의 수명이 끝나지 않으면 참조를 반환해도됩니다.


답변

아냐 아냐 아냐 아냐

악의적 인 것은 동적으로 할당 된 객체를 참조하고 원래 포인터를 잃는 것입니다. 당신 new이 물건을 할 때 당신 은 보장을받을 의무가 있다고 가정합니다 delete.

그러나 예를 들어 operator<<: 참조 반환 해야하는 것을 보거나

cout << "foo" << "bar" << "bletch" << endl ;

작동하지 않습니다.


답변

즉시 사라지지 않고 소유권 이전을 원하지 않는 기존 객체에 대한 참조를 반환해야합니다.

로컬 변수 또는 그와 같은 일부에 대한 참조는 반환하지 않으므로 절대로 반환하지 마십시오.

함수와 무관 한 것에 대한 참조를 리턴 할 수 있으며, 호출 함수가 삭제를 담당하지 않을 것으로 예상됩니다. 일반적인 operator[]기능 의 경우입니다 .

무언가를 생성하는 경우 값 또는 포인터 (일반 또는 스마트)를 반환해야합니다. 호출 함수에서 변수 또는 표현식으로 들어가므로 값을 자유롭게 리턴 할 수 있습니다. 로컬 변수에 대한 포인터는 반환되지 않으므로 절대 반환하지 마십시오.


답변

답변이 만족스럽지 않아서 2 센트를 더하겠습니다.

다음과 같은 경우를 분석해 봅시다.

잘못된 사용법

int& getInt()
{
    int x = 4;
    return x;
}

이것은 분명히 오류입니다

int& x = getInt(); // will refer to garbage

정적 변수와 함께 사용

int& getInt()
{
   static int x = 4;
   return x;
}

정적 변수는 프로그램 수명 기간 동안 존재하기 때문에 이것이 맞습니다.

int& x = getInt(); // valid reference, x = 4

싱글 톤 패턴을 구현할 때도 매우 일반적입니다.

Class Singleton
{
    public:
        static Singleton& instance()
        {
            static Singleton instance;
            return instance;
        };

        void printHello()
        {
             printf("Hello");
        };

}

용법:

 Singleton& my_sing = Singleton::instance(); // Valid Singleton instance
 my_sing.printHello();  // "Hello"

연산자

표준 라이브러리 컨테이너는 참조를 반환하는 연산자의 사용에 크게 의존합니다 (예 :

T & operator*();

다음에서 사용될 수 있습니다

std::vector<int> x = {1, 2, 3}; // create vector with 3 elements
std::vector<int>::iterator iter = x.begin(); // iterator points to first element (1)
*iter = 2; // modify first element, x = {2, 2, 3} now

내부 데이터에 빠르게 액세스

내부 데이터에 빠르게 액세스하기 위해 &를 ​​사용하는 경우가 있습니다

Class Container
{
    private:
        std::vector<int> m_data;

    public:
        std::vector<int>& data()
        {
             return m_data;
        }
}

사용법 :

Container cont;
cont.data().push_back(1); // appends element to std::vector<int>
cont.data()[0] // 1

그러나 이것은 다음과 같은 함정으로 이어질 수 있습니다.

Container* cont = new Container;
std::vector<int>& cont_data = cont->data();
cont_data.push_back(1);
delete cont; // This is bad, because we still have a dangling reference to its internal data!
cont_data[0]; // dangling reference!


답변

악하지 않습니다. C ++의 많은 것들과 마찬가지로 올바르게 사용하면 좋지만 지역 변수에 대한 참조를 반환하는 것과 같이 사용할 때 알아야 할 많은 함정이 있습니다.

그것으로 달성 할 수있는 좋은 것들이 있습니다 (예 : map [name] = “hello world”)


답변

“참조를 반환하는 것은 악의적이다. 왜냐하면 단순히 [이해 한 것처럼] 그것을 삭제하기가 더 쉽기 때문이다”

사실이 아니다. 참조를 반환한다고해서 소유권 의미가있는 것은 아닙니다. 즉, 당신이 이것을하기 때문에 :

Value& v = thing->getTheValue();

… v가 참조하는 메모리를 소유한다는 의미는 아닙니다.

그러나 이것은 끔찍한 코드입니다.

int& getTheValue()
{
   return *new int;
}

“해당 인스턴스에 포인터가 필요하지 않기 때문에 이와 같은 작업을 수행하는 경우 1) 참조가 필요한 경우 포인터를 역 참조하고 2) 결국 포인터가 필요합니다. 새로운 삭제, 당신은 삭제 호출 포인터가 필요합니다.


답변

두 가지 경우가 있습니다.

  • const reference-때로는 무거운 객체 또는 프록시 클래스, 컴파일러 최적화에 대한 좋은 아이디어

  • 비 const 참조-때로는 나쁜 생각으로 캡슐화가 깨짐

둘 다 동일한 문제를 공유합니다-잠재적으로 파괴 된 객체를 가리킬 수 있습니다 …

참조 / 포인터를 반환 해야하는 많은 상황에서 스마트 포인터를 사용하는 것이 좋습니다.

또한 다음 사항에 유의하십시오.

C ++ 표준 (관심있는 경우 섹션 13.3.3.1.4)에 임시 규칙은 const 참조에만 바인딩 할 수 있다고 공식적인 규칙이 있습니다-비 const 참조를 사용하려고하면 컴파일러는이 플래그를 오류.