수업/C++

3강 C++기초 // C++의 구조체, 동적 메모리할당

MDanderson 2024. 10. 1. 01:40

배열

int b[5]= {1,2,3} 하면   나머지 칸들은 0 으로 초기화된다

int c[] = {1,2,3,4,5}  하면 자동으로 크기 5로 정의됨

 

동적메모리할당 : 프로그램동작중에 기억공간의 필요성 및 소요량을 결정하여 필요한 공간을 할당하는 것

 - 기억공간의 생성시점 ;new연산자의 실행시점

 -  기억공간의 소멸시점 : delete연산자의 실행시점

 

포인터 변수가 할당된 기억공간을 가리키게 함

 

메모리 할당 연산자

ptrVar = new TypeName;

ptrVar = new TypeName[n];

 

메모리 반납 연산자

delete ptrVar;

delete [] ptrVar;

 

 

int * intPtr

intPtr = new int; 정수하나를 저장할수있는 메모리를 할당받음

*intPtr =10;

delete intPtr;

intPtr =nullptr;  //c++11부터 도입된 널포인터

 

l-value 참조

참조변수는 참조대상의 별명처럼 사용할 수 있음

l-value  :실체가 있는대상(l-value)에 대한 참조

 

int a=10;

int& aRef = a;

aRef=100;   // a도 100이됨 

 

refVar는 varName를 참조함

참조는 반드시 초기화가 필요하다

a의 별명이 aRef가 된 것.

 

const 참조

 참조변수가 참조하는 대상을 읽게만할수있고 값을 바꿀수는 없음

 

int x {10} ;

const int& xRef =x;

xRef +=10; // 오류

 

 

참조가 포인터와 다른점

참조  변수를 이용하여 값을 읽거나 저장할 때 참조 대상변수를 사용하는 형식과 동일함

참조변수는 초기화를 통해 반드시 어떤 대상을 참조해야함

- 초기화되지않은 상태로 인해 무엇을 참조하고있는지 알수 없는 상황은 발생하지 않음

참조변수는 초기화를 통해 지정된 참조 대상을 바꿀 수 없어 참조의 유효기간 동안 하나의 대상만 참조할 수 있음

 

r-value 참조란?

- 사용한 후에 그 값을 더이상 가지고 있을 필요가 없는 대상 참조

용도: 객체의 값을 다른 객체로 이동

 

 

int arr[10];

for (                   : arr)

      cin >> a;

Q1
위 지문에서 배열의 항목에 cin으로 부터 입력된 값을 넣으려고 한다. 공란에 넣을 내용은?

 

1
int a
2
int& a
3
const int& a
4
int a = 0
정답입니다.
정답 : 2
범위 기반 for 루프에서 arr의 각 원소를 나타내는 변수를 선언한다. 이때 arr의 값을 입력을 통해 변경해야 하므로 참조형으로 선언한다.

 

int a로 하면 문제가있다
범위 기반 for 루프에서 a가 배열의 복사본이라는 점입니다. 범위 기반 for 루프에서 a는 배열 arr의 요소를 복사한 값이므로, 배열 arr의 원소가 직접 수정되지 않습니다.

 

 

C++에서 클래스와 구조체의 차이점을 바르게 설명한 것은?
1
구조체 내에는 함수를 정의할 수 없다.
2
클래스 내에는 자료를 저장하는 항목을 정의할 수 없다.
3
구조체의 구성 요소는 ->으로, 클래스의 구성 요소는 . 으로 지정한다.
4
클래스의 항목들은 공개 여부를 지정하지 않은 경우 공개되지 않는 것이 기본이나, 구조체의 항목들은 공개되는 것이 기본이다.
정답입니다.
정답 : 4
구조체는 내부 데이터 항목들을 외부에서 자유롭게 사용하도록 공개하는 것이 디폴트이다. 반면 클래스의 경우는 내부의 구현을 감추는 것이 디폴트이다.


c++의 경우 구조체내에 함수정의 가능

클래스 내에 자료를 저장하는 항목 정의 가능

구조체와 클래스의 구성 요소(멤버)에 접근하는 방식은 동일합니다.

  • 만약 포인터를 통해 접근할 경우, 구조체와 클래스 모두 -> 연산자를 사용합니다.
  • 포인터가 아닌 객체를 통해 직접 접근할 경우, 구조체와 클래스 모두 . 연산자를 사용합니다.

 

C언어와  C++의 구조체 차이

 

C 언어의 구조체

  • 데이터 묶음: C에서 구조체는 단순히 여러 개의 변수를 묶어서 하나의 데이터 타입으로 사용하는 용도로 사용됩니다.
  • 멤버 함수: C의 구조체는 함수를 포함할 수 없습니다. 구조체는 데이터만을 저장하기 위한 용도입니다.
  • 접근 제어자: C에서는 모든 구조체 멤버가 기본적으로 public입니다. C 언어는 구조체 멤버에 대한 접근 제어를 제공하지 않습니다.

C++의 구조체

  • 멤버 함수: C++에서는 구조체가 데이터뿐만 아니라 함수도 포함할 수 있습니다. 구조체는 클래스와 거의 동일하게 동작하며, 객체지향 프로그래밍을 지원합니다.
  • 접근 제어자: C++에서는 구조체 멤버에 대해 접근 제어자가 존재합니다. 기본적으로 public이지만, private 또는 protected를 지정할 수도 있습니다.
  • 상속: C++의 구조체는 클래스와 동일하게 상속을 지원합니다. 구조체 간 상속 관계를 형성할 수 있으며, 이를 통해 구조체 간에 기능을 공유할 수 있습니다.
  • 생성자 및 소멸자: C++의 구조체는 **생성자(constructor)**와 **소멸자(destructor)**를 가질 수 있습니다. 따라서 객체를 초기화하거나 객체가 소멸될 때 동작을 정의할 수 있습니다.

 

  • C 언어: C의 구조체는 순수한 데이터 묶음으로, 클래스와 같은 개념이 존재하지 않습니다.
  • C++ 언어: C++의 구조체는 사실상 클래스와 거의 동일합니다. 유일한 차이점기본 접근 제어자가 다르다는 점입니다.
    • 구조체의 기본 접근 제어자public입니다.
    • 클래스의 기본 접근 제어자private입니다.

 

  • C 언어: 접근 제어자가 없으므로 모든 멤버가 기본적으로 public입니다. 구조체 외부에서 모든 멤버에 접근할 수 있습니다.
  • C++ 언어: C++에서는 구조체에도 접근 제어자를 사용할 수 있습니다. 멤버 변수를 private, protected, 또는 public으로 지정할 수 있으며, 객체지향 프로그래밍에서 중요한 캡슐화가 가능합니다.

 

  • C 언어: 상속과 다형성은 지원되지 않습니다. C 언어는 객체지향 프로그래밍을 지원하지 않기 때문에 이러한 기능은 사용할 수 없습니다.
  • C++ 언어: C++의 구조체는 상속을 지원하며, 다형성을 구현할 수 있습니다. 구조체는 부모 구조체 또는 클래스로부터 멤버와 메서드를 상속받을 수 있으며, 가상 함수 등을 통해 다형성도 구현 가능합니다.

요약

특징C 구조체C++ 구조체

데이터 단순 데이터 묶음 데이터와 함수 포함 가능
접근 제어자 없음, 모든 멤버가 public public, private, protected 사용 가능
상속 지원되지 않음 클래스처럼 상속 가능
생성자/소멸자 없음 생성자/소멸자 가능
다형성 지원되지 않음 가상 함수 등을 사용하여 다형성 구현 가능
기본 접근 제어자 public 기본 접근 제어자는 public (클래스는 private)

 

 

 

 C++의 new연산자  C언어의 malloc()함수 차이

 

C++에서 new 연산자를 사용하여 동적 메모리를 할당하는 것과 C에서 malloc() 함수를 사용하여 동적 메모리를 할당하는 것 사이에는 몇 가지 중요한 차이점이 있습니다. 이 차이점들은 주로 초기화 여부, 타입 안전성, 소멸자 호출 여부에 관련됩니다. 아래에서 두 가지 방법을 비교하고 차이점을 설명하겠습니다.

C++의 new연산자

 

int* ptrVar = new int;
int* arr = new int[10]; // 배열 동적 할당
  • 기능:
    • new 연산자는 타입에 맞춰 메모리를 할당하고, 해당 타입의 생성자를 호출하여 객체를 초기화합니다.
    • 기본적으로 할당된 메모리 공간이 타입에 맞게 초기화됩니다. 예를 들어, 기본 생성자가 있는 객체의 경우 생성자가 호출됩니다.
    • 배열을 할당할 때는 new[]를 사용하며, 할당된 배열의 각 요소에 대한 기본 생성자가 호출됩니다.
    • 메모리를 해제할 때는 delete와 delete[]를 사용합니다.
    • 타입 안전성: new 연산자는 타입을 명확하게 지정하므로, 타입 변환이 필요 없습니다

int* ptr = new int;  // int형 메모리 공간 할당 및 기본 값 초기화
*ptr = 5;            // ptr이 가리키는 메모리에 값 할당
delete ptr;          // 메모리 해제

 

 

 malloc()함수

 

int* ptrVar = (int*)malloc(sizeof(int));  // 하나의 int 크기만큼 할당
int* arr = (int*)malloc(sizeof(int) * 10); // 배열 동적 할당

 

기능:

  • malloc() 함수는 바이트 단위로 메모리를 할당합니다. 즉, 생성자 호출 없이 메모리만 확보합니다.
  • 할당된 메모리는 초기화되지 않습니다. 즉, 쓰레기 값을 포함할 수 있습니다.
  • 메모리를 해제할 때는 free() 함수를 사용합니다.
  • 타입 안전성: malloc() 함수는 반환 값이 void* 포인터이므로, 타입을 맞추기 위해 명시적 캐스팅이 필요합니다.

int* ptr = (int*)malloc(sizeof(int));  // int형 메모리 공간 할당 (초기화 없음)
*ptr = 5;                             // ptr이 가리키는 메모리에 값 할당
free(ptr);                            // 메모리 해제

 

 

2. 초기화 여부

  • new: 할당된 메모리는 객체의 생성자를 호출하거나, 기본 타입의 경우 0으로 초기화되지 않지만, 직접 new int()와 같이 사용하면 0으로 초기화할 수 있습니다. 즉, 초기화는 옵션입니다.
int* ptr = new int(0); // 메모리 할당 및 0으로 초기화

 

  • malloc(): 할당된 메모리는 초기화되지 않습니다. 만약 초기화된 메모리가 필요하다면, memset() 또는 별도로 값을 지정해야 합니다. 초기화되지 않은 메모리는 쓰레기 값을 가질 수 있습니다.

 

  • int* ptr = (int*)malloc(sizeof(int)); // 초기화되지 않은 메모리 할당
    memset(ptr, 0, sizeof(int)); // 0으로 초기화

3. 타입 안전성

  • new: 타입에 맞게 메모리가 할당되므로 타입 안전성이 보장됩니다. 반환되는 포인터의 타입을 명시적으로 캐스팅할 필요가 없습니다.
     
    int* ptr = new int; // 타입 안전
  • malloc(): malloc()은 **타입을 모르기 때문에 void***를 반환합니다. 따라서 할당된 메모리를 사용하려면 명시적인 캐스팅이 필요합니다.
     
    int* ptr = (int*)malloc(sizeof(int)); // 명시적 캐스팅 필요

4. 생성자 및 소멸자 호출

  • new: 클래스 객체를 동적 할당할 때 생성자가 호출됩니다. 또한, delete를 사용하면 소멸자가 호출되어 객체의 메모리가 안전하게 해제됩니다.
     
    MyClass* obj = new MyClass(); // 생성자 호출
    delete obj; // 소멸자 호출

 

  • malloc(): 생성자나 소멸자와 같은 개념이 없기 때문에, 객체의 생성 및 해제 시 생성자나 소멸자가 호출되지 않습니다. 만약 생성자나 소멸자를 수동으로 호출해야 하는 경우가 있으면 이를 명시적으로 처리해야 합니다.
     
    MyClass* obj = (MyClass*)malloc(sizeof(MyClass)); // 생성자 호출 안 됨

5. 메모리 해제

  • new: 동적으로 할당된 메모리는 반드시 delete 또는 delete[]를 사용해 해제해야 합니다.
     
    delete ptr; // 단일 객체 메모리 해제
    delete[] arr; // 배열 메모리 해제

 

  • malloc(): malloc()로 할당된 메모리는 반드시 free()를 사용해 해제해야 합니다.
     
    free(ptr); // 메모리 해제

요약 비교표

특징                                  C++ new/delete                                                                                           C malloc/free

초기화 여부 기본적으로 생성자가 호출되어 초기화될 수 있음 초기화되지 않음
타입 안전성 타입 안전, 명시적 캐스팅 불필요 void* 반환, 명시적 캐스팅 필요
생성자/소멸자 생성자와 소멸자가 호출됨 생성자와 소멸자가 호출되지 않음
메모리 해제 방법 delete와 delete[] 사용 free() 사용
배열 할당 new[]로 동적 배열 할당 malloc()로 메모리 할당
오류 처리 실패 시 std::bad_alloc 예외 발생 (처리 가능) 실패 시 NULL 반환 (체크 필요)

결론

  • C++에서 new/delete: C++ 스타일의 동적 메모리 할당 및 해제 방식으로, 타입 안전성, 생성자/소멸자 호출, 예외 처리 등의 장점이 있습니다.
  • C에서 malloc()/free(): C 스타일의 동적 메모리 할당 방식으로, 단순히 메모리만 할당하고 초기화나 객체 관리가 따로 필요합니다. 타입 안전성도 보장되지 않으며, 명시적인 캐스팅이 필요합니다.

일반적으로 C++에서는 new/delete를 사용하는 것이 더 안전하고 효율적입니다.

 

 

c++에서도 여전히 **malloc()**를 사용할 수 있지만, 권장되지 않습니다. C++에서 제공하는 new/delete 연산자가 malloc()/free()에 비해 여러 가지 면에서 더 적합하고 안전하기 때문입니다.

  1. 객체 지향 프로그래밍에 맞는 기능:
    • C++의 new 연산자는 생성자 호출을 통해 객체를 초기화합니다. 반대로, malloc()은 단순히 메모리를 할당할 뿐, 생성자를 호출하지 않기 때문에 객체가 제대로 초기화되지 않을 수 있습니다.
    • delete는 소멸자 호출을 통해 객체가 메모리에서 해제될 때 자원을 정리할 수 있도록 합니다. free()는 소멸자를 호출하지 않으므로, 객체가 제대로 소멸되지 않고 리소스 누수가 발생할 가능성이 있습니다.
  2. 타입 안전성:
    • new 연산자는 명시적인 타입 캐스팅이 필요 없기 때문에 타입 안전성을 보장합니다. malloc()은 void*를 반환하므로, 적절한 타입으로 명시적으로 캐스팅해야 하는데, 이 과정에서 오류가 발생할 수 있습니다.
  3. 예외 처리:
    • new는 메모리 할당에 실패할 경우 예외(std::bad_alloc)를 발생시킵니다. 이를 통해 메모리 부족 상황을 명확히 처리할 수 있습니다.
    • 반대로, malloc()은 메모리 할당 실패 시 **NULL**을 반환합니다. 이 반환 값을 매번 확인하고 처리하지 않으면 문제가 발생할 수 있습니다.
  4. RAII (Resource Acquisition Is Initialization):
    • C++는 RAII 개념에 따라 자원(메모리, 파일 핸들 등)의 획득과 초기화를 하나로 묶는 패턴을 장려합니다. new/delete는 이러한 개념을 더 잘 반영합니다. 또한, 스마트 포인터(std::unique_ptr, std::shared_ptr)를 사용하여 동적 메모리를 자동으로 관리할 수 있습니다.

예시 비교

 
// C++ 스타일:
 
new/delete int* ptr = new int(5); // 메모리 할당 및 초기화
std::cout << *ptr << std::endl; delete ptr; // 메모리 해제 //
 
 
C 스타일:
 
malloc/free int* ptr2 = (int*)malloc(sizeof(int)); // 메모리 할당 (초기화 없음)
*ptr2 = 5; // 직접 값 설정
std::cout << *ptr2 << std::endl; free(ptr2); // 메모리 해제

C++에서 malloc()를 사용하는 경우

C++에서도 malloc()를 사용하는 상황이 있을 수 있지만, 이는 주로 레거시 코드나 특정한 이유가 있을 때입니다. 예를 들어, 어떤 C 라이브러리와 상호작용할 때, 그 라이브러리의 메모리 할당 방식이 malloc()를 기반으로 할 경우입니다.

결론

  • C++에서는 new/delete를 사용하는 것이 권장됩니다. 이 방식이 더 안전하고, 객체 지향 프로그래밍의 원칙과 잘 맞으며, 초기화와 소멸자를 지원합니다.
  • malloc()/free()는 여전히 사용 가능하지만, 일반적으로 C++ 코드에서는 new/delete를 사용하거나, 더 나아가 스마트 포인터(std::unique_ptr, std::shared_ptr)를 사용하는 것이 훨씬 더 바람직합니다.