c++ - 포인터 - 동적 클래스 생성




왜 객체 자체가 아닌 포인터를 사용해야합니까? (15)

Java 백그라운드에서 시작하여 C ++로 객체 작업을 시작했습니다. 그러나 나에게 일어난 한 가지 사실은 사람들이 종종 객체 자체보다는 객체에 대한 포인터를 사용한다는 것입니다. 예를 들어 다음과 같습니다.

Object *myObject = new Object;

오히려 :

Object myObject;

함수를 사용하는 대신 testFunc() 라고 testFunc() .

myObject.testFunc();

우리는 다음과 같이 쓴다.

myObject->testFunc();

그러나 나는 왜 우리가 이런 식으로해야 하는지를 알 수 없다. 나는 우리가 메모리 주소에 직접 접근하기 때문에 효율성과 속도와 관련이 있다고 가정한다. 내가 맞습니까?


하지만 왜 이런 식으로 사용해야하는지 모르겠습니다.

사용하는 경우 함수 본문 내에서 어떻게 작동하는지 비교해 보겠습니다.

Object myObject;

이 함수가 돌아 오면 myObject 가 파괴됩니다. 그래서 이것은 당신이 당신의 함수 밖에서 당신의 객체를 필요로하지 않는다면 유용합니다. 이 객체는 현재 스레드 스택에 배치됩니다.

함수 본문 내부에 다음을 쓰면 :

 Object *myObject = new Object;

myObject 가 가리키는 Object 클래스 인스턴스는 일단 함수가 끝나고 힙에 할당되면 파괴되지 않습니다.

이제 자바 프로그래머라면 두 번째 예제는 자바에서 객체 할당이 어떻게 작동하는지에 가깝습니다. 이 줄은 다음과 같습니다. Object *myObject = new Object; java : Object myObject = new Object(); . 차이점은 java myObject에서는 가비지 수집이 발생하지만 C ++에서는 해제되지 않으므로 명시 적으로 'delete myObject'를 호출해야합니다. 그렇지 않으면 메모리 누수가 발생합니다.

C ++ 11 이후로는 shared_ptr / unique_ptr에 값을 저장함으로써 안전한 할당 방법 인 new Objectnew Object 사용할 수 있습니다.

std::shared_ptr<std::string> safe_str = make_shared<std::string>("make_shared");

// since c++14
std::unique_ptr<std::string> safe_str = make_unique<std::string>("make_shared"); 

또한 객체는 맵 또는 벡터와 같이 컨테이너에 저장되는 경우가 많으며 객체의 수명을 자동으로 관리합니다.


머리말

Java는 과대 선전과는 달리 C ++과 거의 같습니다. Java 과대 광고 기계는 Java가 C ++과 유사한 구문을 사용하기 때문에 언어가 비슷하다는 점을 믿고 싶습니다. 진실에서 더 멀어 질 수는 없습니다. 이 잘못된 정보는 Java 프로그래머가 C ++로 이동하여 코드의 의미를 이해하지 않고 Java와 유사한 구문을 사용하는 이유 중 일부입니다.

앞으로 우리는 간다.

그러나 나는 왜 우리가 이런 식으로해야 하는지를 알 수 없다. 나는 우리가 메모리 주소에 직접 접근하기 때문에 효율성과 속도와 관련이 있다고 가정한다. 내가 맞습니까?

반대로, 실제로. 힙은 힙에 비해 매우 간단하기 때문에 스택보다 훨씬 느립니다 . 자동 스토리지 변수 (스택 변수라고도 함)는 범위를 벗어나면 소멸자를 호출합니다. 예 :

{
    std::string s;
}
// s is destroyed here

반면에, 동적으로 할당 된 포인터를 사용한다면, 소멸자는 수동으로 호출되어야합니다. delete 는이 소멸자를 호출합니다.

{
    std::string* s = new std::string;
}
delete s; // destructor called

이것은 C #과 Java에서 널리 사용되는 new 구문과 아무 관련이 없습니다. 그들은 완전히 다른 목적으로 사용됩니다.

동적 할당의 이점

1. 배열의 크기를 미리 알 필요가 없습니다.

많은 C ++ 프로그래머가 처한 첫 번째 문제 중 하나는 사용자가 임의로 입력을 받으면 스택 변수에 고정 된 크기 만 할당 할 수 있다는 것입니다. 배열의 크기도 변경할 수 없습니다. 예 :

char buffer[100];
std::cin >> buffer;
// bad input = buffer overflow

물론 std::string 대신 std::string 을 사용하면 내부적으로 크기가 조정되므로 문제가되지 않습니다. 그러나 본질적으로이 문제에 대한 해결책은 동적 할당입니다. 다음과 같이 사용자 입력에 따라 동적 메모리를 할당 할 수 있습니다.

int * pointer;
std::cout << "How many items do you need?";
std::cin >> n;
pointer = new int[n];

참고 : 많은 초보자가 실수하는 한 가지는 가변 길이 배열을 사용하는 것입니다. 이것은 GNU 확장이며, GCC 확장의 많은 부분을 반영하기 때문에 Clang의 확장입니다. 따라서 다음 int arr[n] 은 신뢰해서는 안됩니다.

힙은 스택보다 훨씬 크기 때문에 스택에 제한이있는 반면 임의로 필요한만큼의 메모리를 할당 / 재 할당 할 수 있습니다.

2. 배열 포인터가 아닙니다.

이것이 당신이 묻는 이익은 무엇입니까? 배열과 포인터 뒤에있는 혼란 / 신화를 이해하면 대답이 명확 해집니다. 일반적으로 그들은 동일하다고 가정되지만 그렇지 않습니다. 이 신화는 포인터가 배열처럼 첨자 될 수 있고 배열이 함수 선언의 최상위 레벨에서 포인터로 쇠퇴하기 때문에 발생합니다. 그러나 배열이 포인터로 붕괴되면 포인터의 크기 정보가 손실됩니다. 따라서 sizeof(pointer)sizeof(pointer) 의 크기를 바이트 단위로 나타내며, 일반적으로 64 비트 시스템에서는 8 바이트입니다.

배열에만 할당 할 수는 없으며 배열 만 초기화 할 수 있습니다. 예 :

int arr[5] = {1, 2, 3, 4, 5}; // initialization 
int arr[] = {1, 2, 3, 4, 5}; // The standard dictates that the size of the array
                             // be given by the amount of members in the initializer  
arr = { 1, 2, 3, 4, 5 }; // ERROR

반면에 포인터로 원하는대로 할 수 있습니다. 불행하게도, 포인터와 배열의 차이점은 자바와 C #에서 손으로 흔들기 때문에, 초보자들은 그 차이를 이해하지 못합니다.

3. 다형성

Java 및 C #에는 오브젝트를 다른 것으로 처리 할 수있는 기능이 있습니다 (예 as 키워드 사용). 따라서 누군가가 Entity 객체를 Player 객체로 취급하고 싶다면 Player player = Entity as Player; 이는 특정 유형에만 적용해야하는 동 질적 컨테이너에서 함수를 호출하려는 경우 매우 유용합니다. 다음과 같은 방식으로 기능을 수행 할 수 있습니다.

std::vector<Base*> vector;
vector.push_back(&square);
vector.push_back(&triangle);
for (auto& e : vector)
{
     auto test = dynamic_cast<Triangle*>(e); // I only care about triangles
     if (!test) // not a triangle
        e.GenericFunction();
     else
        e.TriangleOnlyMagic();
}

Triangles 만 Rotate 함수를 가지고 있다면 클래스의 모든 객체에서 호출하려고하면 컴파일러 오류가 발생합니다. dynamic_cast 사용하면 as 키워드를 시뮬레이트 할 수 있습니다. 명확하게하기 위해 캐스트가 실패하면 잘못된 포인터를 반환합니다. 따라서 !testtest 가 NULL인지 또는 유효하지 않은 포인터인지 확인하기위한 줄임말입니다. 이는 캐스트가 실패했음을 의미합니다.

자동 변수의 이점

동적 할당이 할 수있는 모든 위대한 일을보고 난 후에 왜 동적 할당을 항상 사용하지 않는지 궁금해 할 것입니다. 나는 이미 하나의 이유를 말했고 힙은 느리다. 그리고 그 모든 기억이 필요하지 않으면 그것을 남용해서는 안됩니다. 그래서 특별한 순서없이 몇 가지 단점이 있습니다 :

  • 오류가 발생하기 쉽습니다. 수동 메모리 할당은 위험하므로 누출이 발생하기 쉽습니다. 디버거 또는 valgrind (메모리 누수 도구)를 능숙하게 사용하지 못하면 머리를 머리에서 꺼낼 수 있습니다. 다행스럽게도 RAII 관용구와 똑똑한 포인터가이 점을 조금 완화 해 주지만, 당신은 규칙 3과 규칙 5와 같은 습관에 익숙해야합니다. 들어갈 정보가 많아서 모르는 사람이나 신경 쓰지 않는 초보자가이 함정에 빠지게됩니다.

  • 꼭 필요한 것은 아닙니다. Java 및 C #과 달리 어디서나 new 키워드를 사용하는 관용적 인 기능이 있습니다. C ++에서는 필요할 때만 사용해야합니다. 일반적인 문구는 망치가 있으면 모든 것이 못처럼 보입니다. C ++로 시작하는 초보자는 포인터를 두려워하고 습관으로 스택 변수를 사용하는 법을 배우는 반면 Java 및 C # 프로그래머는 포인터를 이해하지 않고 시작 합니다. 그것은 말 그대로 잘못된 발로 발을 디디고 있습니다. 문법이 하나이기 때문에 당신이 알고있는 모든 것을 포기해야합니다. 언어를 배우는 것이 또 다른 것입니다.

1. (N) RVO - 일명, (Named) 반환 값 최적화

많은 컴파일러가 수행하는 최적화 중 하나가 elision반환 값 최적화 라고하는 것입니다. 이러한 것들은 불필요한 copys를 제거 할 수 있습니다. 이것은 많은 요소를 포함하는 벡터와 같이 매우 큰 객체에 유용합니다. 일반적으로 포인터를 사용하여 큰 개체를 복사하는 대신 포인터를 사용하여 소유권이전 합니다. 이것은 이동 의미론스마트 포인터 의 시작으로 이어진다.

포인터를 사용하는 경우 (N) RVO가 발생하지 않습니다 . 최적화에 대해 걱정할 경우 포인터를 반환하거나 전달하는 것보다 (N) RVO를 이용하는 것이 더 유용하고 에러가 발생하기 쉽지 않습니다. 함수의 호출자가 동적으로 할당 된 객체 등을 delete 해야하는 경우 오류 누출이 발생할 수 있습니다. 포인터가 고구마와 같이 지나가고 있다면 객체의 소유권을 추적하기가 어려울 수 있습니다. 스택 변수를 사용하는 것이 더 쉽고 간단하기 때문입니다.


C ++은 포인터, 참조 및 값에 의해 객체를 전달하는 세 가지 방법을 제공합니다. Java는 후자의 경우를 제한합니다 (유일한 예외는 int, boolean 등의 기본 유형). 이상한 장난감처럼 C ++을 사용하고 싶지 않다면이 세 가지 방법의 차이점을 알아야합니다.

자바는 '누가 언제 이것을 없애야 하는가?'와 같은 문제가 없다고 주장한다. 대답은 다음과 같습니다. 가비지 수집가. 그럼에도 불구하고 메모리 누수에 대해 100 % 보호 할 수는 없습니다 (예, Java 메모리 누출 시킬 수 있음). 실제로 GC는 잘못된 안전감을줍니다. SUV가 클수록 대피자가 길어집니다.

C ++은 객체의 라이프 사이클 관리와 대면하게됩니다. 글쎄, 스마트 포인터 패밀리, Qt의 QObject 등을 다루는 방법이 있지만, 그것들 중 어느 것도 GC와 같이 '불과 잊기'방식으로 사용할 수는 없습니다. 항상 메모리 처리를 염두에 두어야합니다. 객체를 파기하는 것뿐만 아니라 동일한 객체를 두 번 이상 파기하지 않아야합니다.

아직 두렵지 않니? 확인 : 순환 참조 - 직접 처리하십시오. 그리고 기억하십시오 : 각 객체를 정확하게 한 번만 죽이면 C ++ 런타임은 시체를 엉망으로 만든 자, 죽은자를 혼자 남겨 두는자를 좋아하지 않습니다.

다시 질문에 답하십시오.

객체를 포인터 나 참조가 아닌 값으로 전달할 때 객체를 복사합니다 (전체 객체, 바이트 또는 거대한 데이터베이스 덤프 등). 후자를 피하기 위해 충분히 똑똑합니다. t?) = '할 때마다. 그리고 객체의 멤버에 액세스하려면 '.'을 사용합니다. (점).

객체를 포인터로 전달하면 몇 바이트 (32 비트 시스템에서는 4 개, 64 비트 객체에서는 8 개) 만 복사합니다. 즉,이 객체의 주소입니다. 그리고 이것을 모든 사람들에게 보여주기 위해, 당신은 멤버에게 접근 할 때이 멋진 '->'연산자를 사용합니다. 또는 '*'와 '.'의 조합을 사용할 수 있습니다.

참조를 사용할 때, 값을 가장하는 포인터를 얻습니다. 그것은 포인터지만 '.'을 통해 멤버에 액세스합니다.

그리고 한 번 더 생각해보십시오. 여러 변수를 쉼표로 구분하여 선언하면 손을 볼 수 있습니다.

  • 모든 사람에게 유형이 부여됩니다.
  • 값 / 포인터 / 참조 수정자는 개별 항목입니다.

예:

struct MyStruct
{
    int* someIntPointer, someInt; //here comes the surprise
    MyStruct *somePointer;
    MyStruct &someReference;
};

MyStruct s1; //we allocated an object on stack, not in heap

s1.someInt = 1; //someInt is of type 'int', not 'int*' - value/pointer modifier is individual
s1.someIntPointer = &s1.someInt;
*s1.someIntPointer = 2; //now s1.someInt has value '2'
s1.somePointer = &s1;
s1.someReference = s1; //note there is no '&' operator: reference tries to look like value
s1.somePointer->someInt = 3; //now s1.someInt has value '3'
*(s1.somePointer).someInt = 3; //same as above line
*s1.somePointer->someIntPointer = 4; //now s1.someInt has value '4'

s1.someReference.someInt = 5; //now s1.someInt has value '5'
                              //although someReference is not value, it's members are accessed through '.'

MyStruct s2 = s1; //'NO WAY' the compiler will say. Go define your '=' operator and come back.

//OK, assume we have '=' defined in MyStruct

s2.someInt = 0; //s2.someInt == 0, but s1.someInt is still 5 - it's two completely different objects, not the references to the same one

기술적으로 이것은 메모리 할당 문제이지만, 여기에는 두 가지 실제적인 측면이 있습니다. 그것은 두 가지와 관계가 있습니다 : 1) 범위, 포인터가없는 객체를 정의하면 정의 된 코드 블록 이후에 더 이상 객체에 액세스 할 수 없지만 "new"로 포인터를 정의하면 동일한 포인터에서 "delete"를 호출 할 때까지이 메모리에 대한 포인터가있는 곳이면 어디서나 액세스 할 수 있습니다. 2) 함수에 인수를 전달하려면보다 효율적으로 포인터 또는 참조를 전달해야합니다. Object를 전달하면 객체가 복사됩니다. 많은 양의 메모리를 사용하는 객체 인 경우 CPU 소모 일 수 있습니다 (예 : 벡터로 전체 데이터 복사). 패스 할 때 당신이 건네주는 것은 모두 하나의 int입니다 (구현에 따라 다르지만 대부분 int입니다).

그 외에도 "새로운"은 힙에 메모리를 할당하여 어느 시점에서 해제해야한다는 것을 이해해야합니다. "new"를 사용할 필요가없는 경우 "스택에"일반 객체 정의를 사용하는 것이 좋습니다.


앞으로의 선언, 다형성 등의 중요한 유스 케이스를 포함하여이 질문에 대한 여러 가지 훌륭한 해답이 있지만 질문의 "영혼"의 일부가 응답되지 않는다고 느낍니다. 즉 Java와 C ++에서 서로 다른 구문이 의미하는 바가 있습니다.

두 언어를 비교하는 상황을 살펴 보겠습니다.

자바:

Object object1 = new Object(); //A new object is allocated by Java
Object object2 = new Object(); //Another new object is allocated by Java

object1 = object2; 
//object1 now points to the object originally allocated for object2
//The object originally allocated for object1 is now "dead" - nothing points to it, so it
//will be reclaimed by the Garbage Collector.
//If either object1 or object2 is changed, the change will be reflected to the other

이에 가장 근접한 것은 다음과 같습니다 :

C ++ :

Object * object1 = new Object(); //A new object is allocated on the heap
Object * object2 = new Object(); //Another new object is allocated on the heap
delete object1;
//Since C++ does not have a garbage collector, if we don't do that, the next line would 
//cause a "memory leak", i.e. a piece of claimed memory that the app cannot use 
//and that we have no way to reclaim...

object1 = object2; //Same as Java, object1 points to object2.

대체 C ++ 방식을 보겠습니다.

Object object1; //A new object is allocated on the STACK
Object object2; //Another new object is allocated on the STACK
object1 = object2;//!!!! This is different! The CONTENTS of object2 are COPIED onto object1,
//using the "copy assignment operator", the definition of operator =.
//But, the two objects are still different. Change one, the other remains unchanged.
//Also, the objects get automatically destroyed once the function returns...

C ++이 객체에 대한 포인터 나 객체 자체를 처리 할 수있는 반면, Java (암묵적으로)는 객체에 대한 포인터를 처리합니다. 예외가 있습니다. 예를 들어 Java "원시"유형을 선언하면 포인터가 아닌 실제 값이 복사됩니다. 그래서,

자바:

int object1; //An integer is allocated on the stack.
int object2; //Another integer is allocated on the stack.
object1 = object2; //The value of object2 is copied to object1.

즉, 포인터를 사용하는 것은 꼭 정확하거나 잘못된 방법으로 처리 할 수있는 것은 아닙니다. 그러나 다른 대답은 만족스럽게 그것을 커버했다. 일반적인 생각은 C ++에서 객체의 수명과 어디에 살게 될지에 대해 훨씬 더 많은 통제를한다는 것입니다.

홈 지점 가져 오기 - Object * object = new Object() 구문은 실제로 일반적인 Java (또는 C #의 경우) 의미에 가장 가까운 개념입니다.


포인터를 사용하는 또 하나의 좋은 이유는 forward 선언에 대한 것입니다. 충분히 큰 프로젝트에서 컴파일 시간을 단축 할 수 있습니다.


포인터로 ,

  • 직접 메모리와 대화 할 수 있습니다.

  • 포인터를 조작하여 프로그램의 많은 메모리 누수를 방지 할 수 있습니다.


객체에 대한 포인터를 사용하면 많은 이점이 있습니다.

  1. 효율성 (이미 지적했듯이). 객체를 함수에 전달한다는 것은 객체의 새로운 복사본을 만드는 것을 의미합니다.
  2. 타사 라이브러리의 객체 작업. 개체가 제 3 자 코드에 속하고 저자가 포인터를 통해서만 개체의 사용을 의도 한 경우 (복사 생성자 없음)이 개체를 전달할 수있는 유일한 방법은 포인터를 사용하는 것입니다. 값을 전달하면 문제가 발생할 수 있습니다. (딥 복사 / 얕은 복사 문제).
  3. 오브젝트가 자원을 소유하고 소유권을 다른 오브젝트와 함께 sahred하지 않으려는 경우.

"필요성은 발명의 어머니 다." 내가 지적하고자하는 가장 중요한 차이점은 코딩 경험에 대한 결과입니다. 때로는 객체를 함수에 전달해야합니다. 이 경우 객체가 매우 큰 클래스 인 경우 객체를 전달하면 해당 객체의 상태가 복사됩니다 (객체를 복사하는 오버 헤드가 발생할 수 있음). 그러면 포인터가 고정됩니다. 4 바이트 크기 (32 비트라고 가정). 다른 이유는 이미 위에서 언급 한 ...


가장 중요한 질문은 객체 자체가 아닌 포인터를 사용해야하는 이유무엇입니까? 그리고 내 대답은 C ++에 references 가 있기 때문에 포인터 대신 객체 대신 포인터를 사용하면 안됩니다. 포인터 는 포인터보다 안전하며 포인터보다 안전합니다.

질문에 언급 한 또 다른 사항 :

Object *myObject = new Object;

어떻게 작동합니까?그것은 Object유형의 포인터를 만들고 , 하나의 객체에 맞게 메모리를 할당하고 기본 생성자를 호출합니다. 하지만 실제로는 좋지 않습니다. 동적으로 메모리 (키워드 사용 new)를 할당 하면 수동으로 메모리를 비워야합니다. 즉, 코드에서 다음을 가져야합니다.

delete myObject;

이것은 소멸자를 호출하고 메모리를 해제합니다. 그러나 큰 프로젝트에서는 하나의 스레드가 메모리를 해제했는지 여부를 감지하기가 어려울 수 있지만 이러한 목적으로 공유 포인터를 시도 할 수 는 있지만 성능이 약간 저하되지만 작업하기가 훨씬 쉽습니다. 그들.

그리고 이제 몇 가지 소개가 끝나고 질문으로 돌아갑니다.

함수 대신 데이터를 전송하는 동안 객체 대신 포인터를 사용하여 성능을 향상시킬 수 있습니다.

당신은 std::string(객체이기도하다) 큰 XML과 같은 많은 양의 데이터를 포함하고있다. 이제는 파싱 할 필요가 있지만, void foo(...)다른 방법으로 선언 할 수있는 함수 가있다.

  1. void foo(std::string xml); 이 경우 변수의 모든 데이터를 함수 스택에 복사하면 시간이 걸리므로 성능이 떨어집니다.
  2. void foo(std::string* xml);이 경우 size_t변수 를 전달하는 것과 동일한 속도로 객체에 포인터를 전달 하지만 NULL포인터 또는 잘못된 포인터를 전달할 수 있으므로이 선언은 오류가 발생하기 쉽습니다 . C참조가 없기 때문에 일반적으로 사용되는 포인터 .
  3. void foo(std::string& xml); 여기에서 참조를 전달하지만 기본적으로 포인터를 전달하는 것과 동일하지만 컴파일러는 일부 작업을 수행하며 잘못된 참조를 전달할 수 없습니다. 실제로 잘못된 참조로 상황을 만들 수도 있지만 컴파일러를 속이는 것입니다.
  4. void foo(const std::string* xml); 두 번째와 동일합니다. 포인터 값은 변경할 수 없습니다.
  5. void foo(const std::string& xml); 세 번째와 같지만 개체 값을 변경할 수 없습니다.

더 내가 언급 할 무엇, 당신은 상관없이 (로 선택한하는 할당 방식으로 데이터를 전달하지 이러한 5 가지 방법을 사용할 수 있습니다 new또는 일반 ).

언급 할 또 다른 것은 , 당신이 규칙적인 방법으로 객체를 생성 할 때 , 당신은 스택에 메모리를 할당하지만, 당신이 new힙을 할당 하면서 그것을 생성하는 동안 . 스택을 할당하는 것이 훨씬 빠르지 만, 실제로는 큰 데이터 배열에 대해서는 조금 작습니다. 따라서 큰 개체가 필요한 경우 힙을 사용해야합니다. 스택 오버플로가 발생할 수 있기 때문입니다. 그러나 일반적으로이 문제는 STL 컨테이너를 사용하여 해결 되고 기억됩니다 std::string또한 컨테이너, 일부 사람들은 그것을 잊었 :)


당신 은해서는 안됩니다 . 사람 (많은 사람들, 슬프게도)은 그것을 무지에서 씁니다.

때로는 동적 할당이 있지만 그 예는 잘못되었습니다 .

효율성에 대해 생각해보고 싶다면 좋은 이유없이 간접 참조가 발생하기 때문에 이것은 더 나쁩니다 . 프로그램의 이러한 종류는 느린 하고 더 오류가 발생하기 쉬운 .


메모리 사용률이 높은 영역에서는 포인터가 편리합니다. 예를 들어, 수천 개의 노드가 재귀 루틴을 사용하여 생성되고 이후에 게임에서 다음 최적의 이동을 평가하는 데 사용되는 minimax 알고리즘을 고려하면 할당 취소 또는 재설정 기능 (스마트 포인터 에서처럼)은 메모리 소비를 크게 줄입니다. 포인터가 아닌 변수는 반복 호출이 값을 반환 할 때까지 계속 공간을 차지합니다.


이것은 오랫동안 논의되어 왔지만, Java에서는 모든 것이 포인터입니다. 스택과 힙 할당을 구분하지 않으므로 (모든 객체가 힙에 할당 됨) 포인터를 사용하고 있다는 것을 알지 못합니다. C ++에서는 메모리 요구 사항에 따라 두 가지를 혼합 할 수 있습니다. 성능과 메모리 사용은 C ++ (결정)에서보다 결정적입니다.


포인터는 객체의 메모리 위치를 직접 참조합니다. Java에는 이와 같은 것이 없습니다. Java에는 해시 테이블을 통해 객체의 위치를 ​​참조하는 참조가 있습니다. 이러한 참조를 사용하여 Java에서 포인터 연산과 같은 작업을 수행 할 수 없습니다.

귀하의 질문에 대답하기 위해, 그것은 단지 귀하의 취향입니다. 나는 자바와 비슷한 문법을 ​​선호한다.


Object *myObject = new Object;

이렇게하면 메모리 누수 를 피하기 위해 명시 적으로 삭제해야하는 힙에 대한 객체에 대한 참조가 만들어집니다 .

Object myObject;

이렇게하면 객체 (myObject)가 범위를 벗어날 때 자동으로 삭제 되는 자동 유형의 객체 (myObject)가 스택에 생성됩니다.





c++11