Загружаем каталог…
Загружаем каталог…
4주차에는 객체 포인터와 객체 배열, 동적 메모리 할당을 학습했다. new 와 delete 를 이용하여 실행 중에 필요한 만큼 메모리를 할당하고 반환하는 방법과 this 포인터, string 클래스의 주요 함수에 대해서도 학습했다. 1. 객체 포인터 객체 포인터는 객체의 주소를 저장하는 포인터 변수 이다. 일반 변수의 주소를 포인터에 저장했던 것처럼 객체의 주소 역시 해당 클래스 타입의 포인터에 저장할 수 있다. 예시 Circle donut; Circle *p; p = &donut; p 에는 donut 객체의 주소가 저장되므로 p 를 이용하여 donut 객체의 멤버에 접근할 수 있다. . 과 -> 일반 객체에서 멤버에 접근할 때는 . 연산자를 사용하지만, 객체 포인터를 통해 멤버에 접근할 때는 -> 연산자 를 사용한다. 예시 Circle donut; Circle *p = &donut; donut.getArea(); p->getArea(); (*p).getArea(); p->getArea() 와 (*p).getArea() 는 같은 의미이다. 객체 → . 객체 포인터 → -> 2. 객체 배열 같은 클래스의 객체를 여러 개 저장해야 할 때 객체 배열 을 사용할 수 있다. 일반적인 배열과 선언 방법은 동일하지만, 배열의 각 원소가 하나의 객체가 된다는 차이가 있다. 예시 int n[3]; // int형 배열 Circle circles[3]; // Circle 객체 배열 circles[0] , circles[1] , circles[2] 가 각각 하나의 Circle 객체가 된다. 객체 배열과 생성자 객체 배열을 생성하면 배열의 각 원소에 대해 생성자가 호출된다. 예시 Circle circles[3]; 위와 같이 객체 배열을 생성하면 세 객체에 대해 기본 생성자가 각각 호출된다. circles[0] → Circle() circles[1] → Circle() circles[2] → Circle() 따라서 객체 배열을 생성하기 위해서는 호출할 수 있는 기본 생성자 가 필요하다. 예시 class Circle { public: Circle(int r) { radius = r; } }; Circle circles[3]; // 오류 Circle(int r) 만 직접 정의하면 컴파일러가 기본 생성자 Circle() 을 자동으로 만들어주지 않는다. 따라서 Circle circles[3] 에서 각 객체를 생성할 기본 생성자가 없어 오류가 발생한다. 배열을 생성하면서 각 객체가 사용할 생성자를 직접 지정할 수도 있다. 예시 Circle circles[3] = { Circle(10), Circle(20), Circle() }; 객체 배열이 소멸될 때는 각 객체의 소멸자가 생성의 반대 순서 로 호출된다. 생성 : circles[0] → circles[1] → circles[2] 소멸 : circles[2] → circles[1] → circles[0] 3. 객체 배열과 포인터 배열의 이름이 첫 번째 원소의 주소를 나타내는 것처럼 객체 배열도 포인터를 이용하여 접근할 수 있다. 예시 Circle circles[3]; Circle *p = circles; cout << p->getArea() << endl; p++; cout << p->getArea() << endl; 처음에는 p 가 circles[0] 을 가리키고 있다. p++ 을 실행하면 다음 객체인 circles[1] 을 가리키게 된다. circles[0] circles[1] circles[2] ↑ p p++ circles[0] circles[1] circles[2] ↑ p 4. 동적 메모리 할당 프로그램을 작성하는 시점에 필요한 메모리의 크기를 미리 정하는 것을 정적 할당 이라고 한다. 반면 필요한 메모리의 크기를 미리 알 수 없는 경우에는 프로그램 실행 중에 필요한 만큼 메모리를 할당할 수 있다. 이를 동적 메모리 할당 이라고 한다. C++에서는 동적 메모리를 할당하고 반환하기 위해 new 와 delete 연산자를 사용한다. new : 힙(heap) 메모리에 필요한 공간을 할당 delete : new 로 할당받은 메모리를 반환 예시 int *p = new int; *p = 5; cout << *p << endl; delete p; new int 를 통해 int 하나를 저장할 공간을 동적으로 할당하고, 그 주소를 p 에 저장한다. 사용이 끝난 뒤에는 delete 를 이용하여 해당 메모리를 반환한다. 5. 배열의 동적 할당 배열 역시 new 를 이용하여 동적으로 생성할 수 있다. 예시 int n; cin >> n; int *arr = new int[n]; 배열의 크기 n 을 사용자에게 입력받기 때문에 프로그램을 작성할 때 배열의 크기를 미리 정하지 않아도 된다. 동적으로 생성한 배열을 반환할 때는 일반 delete 가 아니라 delete[] 를 사용해야 한다. 예시 int *p = new int[5]; // 배열 사용 delete[] p; 하나의 데이터를 동적 할당했다면 delete , 배열을 동적 할당했다면 delete[] 를 사용한다. new ↔ delete new [] ↔ delete [] new 로 할당하지 않은 메모리를 delete 하거나 이미 반환한 메모리를 다시 delete 해서는 안 된다. 잘못된 예시 int n; int *p = &n; delete p; // X p 가 가리키는 공간은 new 를 이용하여 동적으로 할당받은 공간이 아니기 때문에 delete 할 수 없다. 6. 객체의 동적 생성 기본 자료형뿐만 아니라 객체도 동적으로 생성 할 수 있다. 예시 Circle *p = new Circle(30); cout << p->getArea() << endl; delete p; new Circle(30) 을 실행하면 객체를 위한 메모리가 할당되고 Circle(int r) 생성자가 호출된다. 이후 delete p 를 실행하면 객체의 소멸자가 호출된 뒤 메모리가 반환된다. new Circle(30) ↓ 메모리 할당 ↓ 생성자 호출 delete p ↓ 소멸자 호출 ↓ 메모리 반환 일반적인 지역 객체는 범위를 벗어나면 자동으로 소멸하지만, new 를 이용하여 생성한 객체는 사용이 끝난 뒤 직접 delete 를 해주어야 한다. 7. 객체 배열의 동적 생성 객체 배열 역시 동적으로 생성할 수 있다. 예시 int n; cin >> n; Circle *circles = new Circle[n]; 사용자가 입력한 n 의 값만큼 Circle 객체가 생성된다. 각 객체에 대해서는 기본 생성자가 호출된다. 동적으로 생성한 객체 배열은 일반 객체 배열과 동일하게 사용할 수 있다. 예시 circles[0].setRadius(10); circles[1].setRadius(20); cout << circles[0].getArea() << endl; 포인터 방식으로도 접근할 수 있다. circles->setRadius(10); (circles + 1)->setRadius(20); 사용이 끝나면 delete[] 를 이용하여 반환한다. delete[] circles; 이때 배열에 있는 각 객체의 소멸자가 배열의 마지막 객체부터 역순으로 호출 된다. 8. 메모리 누수 동적으로 할당한 메모리를 더 이상 사용할 수 없는데도 반환하지 않은 상태를 메모리 누수(memory leak) 라고 한다. 예시 Circle *p = new Circle(10); // 객체 사용 // delete p;가 없음 new 를 이용하여 메모리를 할당했지만 delete 로 반환하지 않았다. 따라서 동적으로 할당한 메모리는 더 이상 필요하지 않을 때 적절하게 반환해야 한다. 9. this 포인터 this 는 현재 객체 자신을 가리키는 포인터 이다. 개발자가 직접 선언하는 변수가 아니라 클래스의 멤버 함수에서 사용할 수 있도록 컴파일러가 제공한다. 예시 class Circle { private: int radius; public: void setRadius(int radius) { this->radius = radius; } }; 여기서 this->radius → 현재 객체의 멤버 변수 radius → 함수의 매개변수 를 의미한다. this가 필요한 경우 특히 멤버 변수와 매개변수의 이름이 같은 경우 this 를 사용하면 둘을 명확하게 구분할 수 있다. 예시 void setRadius(int radius) { radius = radius; } 이렇게 작성하면 두 radius 모두 매개변수를 의미한다. 따라서 멤버 변수에 값을 저장하려면 다음과 같이 작성해야 한다. void setRadius(int radius) { this->radius = radius; } 각 객체는 자신의 this 를 가지므로 같은 멤버 함수를 호출하더라도 this 가 가리키는 객체는 서로 다르다. 10. string 클래스 C++의 string 클래스는 문자열을 저장하고 처리하기 위한 다양한 기능을 제공한다. 기본적인 문자열 입출력은 앞선 주차에서도 사용했기 때문에 이번에는 문자열 처리에 사용하는 주요 멤버 함수를 중심으로 정리했다. length() length() 는 문자열의 길이를 반환한다. 예시 string str = "Hello"; cout << str.length() << endl; 결과는 5 가 된다. substr() substr() 는 문자열의 일부를 잘라 새로운 문자열로 반환한다. 예시 string str = "Hello, Everyone!"; cout << str.substr(0, 5) << endl; cout << str.substr(7) << endl; substr(x, y) 는 x번째 인덱스부터 y개의 문자 를 반환한다. substr(x) 는 x번째 인덱스부터 문자열의 마지막까지 반환한다. string 역시 객체이기 때문에 포인터를 이용하거나 동적으로 생성할 수 있다. 예시 string *p = new string("C++"); p->append(" Great!!"); cout << *p << endl; delete p; p 가 string 객체를 가리키므로 멤버 함수에 접근할 때 -> 를 사용한다. Lab 4_1 - 동적 Dog 객체 배열 첫 번째 과제에서는 Dog 클래스를 정의하고 사용자가 입력한 강아지 수에 따라 Dog 객체 배열을 동적으로 생성 했다. 이번 과제는 3주차에서 학습한 클래스, 생성자와 소멸자, Getter/Setter에 이번 주에 배운 this , 객체 배열, 동적 메모리 할당을 함께 적용하는 문제였다. Dog 클래스 Dog 클래스에는 다음 네 가지 정보를 private 멤버 변수로 저장했다. class Dog { private: string name; int age; double weight; bool boosterShot; // ... }; 강아지의 예방접종 여부는 입력할 때는 y/n 문자열을 사용하지만, 클래스 내부에서는 bool 타입으로 저장한다. this를 이용한 Setter 이번 과제에서는 모든 함수 정의에서 this 를 사용했다. Setter에서도 매개변수와 멤버 변수의 이름이 같기 때문에 this 를 이용하여 둘을 구분할 수 있다. void Dog::setName(string name) { this->name = name; } void Dog::setAge(int age) { this->age = age; } void Dog::setWeight(double weight) { this->weight = weight; } void Dog::setBoosterShot(bool boosterShot) { this->boosterShot = boosterShot; } Getter에서도 현재 객체의 멤버에 접근할 때 this 를 사용할 수 있다. string Dog::getName() { return this->name; } int Dog::getAge() { return this->age; } 예방접종 여부 처리 예방접종 여부는 사용자에게 문자열 y/n 으로 입력받은 뒤 bool 값으로 변환하여 저장했다. string booster; cin >> booster; if (booster.compare("y") == 0) this->boosterShot = true; else this->boosterShot = false; 문자열 비교에는 compare() 를 사용했다. str1.compare(str2) == 0 이면 두 문자열이 같다는 의미이다. 출력할 때는 bool 값을 그대로 출력하는 대신 접종 여부에 따라 O 또는 X 를 출력하도록 했다. if (this->boosterShot) cout << "O" << endl; else cout << "X" << endl; Dog 객체 배열 동적 생성 이번 과제의 핵심은 사용자가 입력한 강아지 수만큼 객체를 생성하는 부분이었다. int n; cout << "How many dogs?"; cin >> n; Dog *dogs = new Dog[n]; 강아지 수가 프로그램을 실행하기 전에는 정해져 있지 않기 때문에 고정된 크기의 객체 배열 대신 new 를 이용하여 필요한 만큼 동적으로 생성했다. 각 객체의 정보는 반복문을 이용하여 입력받을 수 있다. for (int i = 0; i < n; i++) { dogs[i].inforInput(i + 1); } 출력 역시 반복문을 이용하여 각 객체의 writeOutput() 을 호출한다. for (int i = 0; i < n; i++) { dogs[i].writeOutput(); } 프로그램의 전체적인 흐름은 다음과 같다. 강아지 수 n 입력 ↓ new Dog[n] ↓ Dog 객체 n개 생성 ↓ 반복문으로 정보 입력 ↓ 반복문으로 정보 출력 ↓ 조건에 맞는 Dog 탐색 ↓ delete[] dogs 조건에 맞는 객체 찾기 마지막에는 두 살보다 많고 예방접종을 하지 않은 강아지 의 이름을 출력했다. for (int i = 0; i < n; i++) { if (dogs[i].getAge() > 2 && dogs[i].getBoosterShot() == false) { cout << dogs[i].getName() << endl; } } 객체의 멤버 변수가 private 이기 때문에 main() 에서는 직접 접근하지 않고 Getter를 이용하여 조건을 확인했다. 동적으로 생성한 객체 배열을 모두 사용한 뒤에는 메모리를 반환한다. delete[] dogs; 이 과정에서 배열에 포함된 각 Dog 객체의 소멸자가 호출된다. Lab 4_2 - string과 객체 포인터 두 번째 과제에서는 문자열을 입력받아 length() 와 substr() 을 이용하여 문자열을 처리했다. 첫 번째 문자열은 일반적인 string 객체를 이용하고, 두 번째 문자열은 string 객체의 포인터 를 이용하여 같은 작업을 수행했다. 문자열에 공백이 포함될 수 있기 때문에 입력에는 getline() 을 사용했다. string str; getline(cin, str); substr()을 이용한 문자열 처리 substr() 을 이용하면 문자열을 두 부분으로 나누어 다시 연결할 수 있다. str.substr(i) + str.substr(0, i) 예를 들어 다음 문자열이 있다고 하자. Hello i = 2 라면, str.substr(2) // "llo" str.substr(0, 2) // "He" 가 되므로 두 문자열을 연결하면 lloHe 가 된다. 이를 반복문과 함께 사용하면 시작 위치를 한 칸씩 이동시키며 문자열을 출력할 수 있다. for (int i = 0; i < str.length(); i++) { cout << str.substr(i) + str.substr(0, i) << endl; } string 객체 포인터 두 번째 문자열에서는 일반 객체가 아니라 string 객체의 포인터를 이용했다. string *p = new string; getline(cin, *p); p 는 string 객체를 가리키고 있기 때문에 length() 와 substr() 에 접근할 때 -> 를 사용한다. for (int i = 0; i < p->length(); i++) { cout << p->substr(i) + p->substr(0, i) << endl; } 사용이 끝난 뒤에는 동적으로 생성한 string 객체를 반환한다. delete p; 첫 번째 문자열과 두 번째 문자열은 같은 작업을 수행하지만 접근 방법에는 다음과 같은 차이가 있다. string 객체 str.length() str.substr() ↓ string 객체 포인터 p->length() p->substr() 이를 통해 객체 자체를 사용할 때와 객체 포인터를 사용할 때 멤버에 접근하는 방법의 차이를 다시 확인할 수 있었다. 마무리 4주차에는 3주차에서 배운 클래스와 객체를 확장하여 객체 포인터와 객체 배열을 다루는 방법을 학습했다. 일반 객체에서는 . 을 사용하지만 객체 포인터에서는 -> 를 사용하여 멤버에 접근 한다는 점과, 객체 배열을 생성하면 배열의 각 객체마다 생성자와 소멸자가 호출된다는 점을 확인했다. 또한 new 와 delete 를 이용 하면 프로그램 실행 중 필요한 만큼 메모리를 동적으로 할당하고 반환 할 수 있다는 것을 배웠다. 특히 하나의 객체에는 delete , 배열에는 delete[] 를 사용 해야 하며 동적으로 할당한 메모리를 적절히 반환하지 않으면 메모리 누수가 발생할 수 있다는 점이 중요했다. 첫 번째 과제에서는 사용자 입력에 따라 Dog 객체 배열을 동적으로 생성하면서 this , Getter/Setter, 객체 배열, new 와 delete[] 를 함께 적용했다. 두 번째 과제에서는 ** string 의 length() 와 substr() 을 활용 하고 같은 문자열 처리 작업을 일반 객체와 객체 포인터 방식으로 각각 구현했다. 이번 주차를 통해 하나의 객체를 정의하고 사용하는 것에서 나아가, 여러 객체를 효율적으로 관리하고 실행 중 필요한 만큼 객체를 동적으로 생성하는 방법 까지 확장하여 이해할 수 있었다.
То, что RADAR обнаружил и классифицировал для этой возможности. Это опубликованный источником текст, а не подтверждение, что предложение ещё действует.
[C++ 고급프로그래밍] Week4. 4주차에는 객체 포인터와 객체 배열, 동적 메모리 할당을 학습했다. new 와 delete 를 이용하여 실행 중에 필요한 만큼 메모리를 할당하고 반환하는 방법과 this 포인터, string 클래스의 주요 함수에 대해서도 학습했다. 1. 객체 포인터 객체 포인터는 객체의 주소를 저장하는 포인터 변수 이다. 일반 변수의 주소를 포인터에 저장했던 것처럼 객체의 주소 역시 해당 클래스 타입의 포인터에 저장할 수 있다. 예시 Circle donut; Circle *p; p = &donut; p 에는 donut 객체의 주소가 저장되므로 p 를 이용하여 donut 객체의 멤버에 접근할 수 있다. . 과 -> 일반 객체에서 멤버에 접근할 때는 . 연산자를 사용하지만, 객체 포인터를…
Открыть источник