CH04 객체 포인터와 객체 배열, 객체의 동적 생성

jiyoon·2024년 4월 24일

1. 객체의 멤버 함수를 호출하는 주요 방법

  1. 직접 객체 사용
  Oval a;
  a.show();  // 객체 a의 show() 메서드 직접 호출
  1. 포인터 사용: 객체의 주소를 가리키는 포인터를 통해 멤버 함수 호출
  Oval* p = &a;
  p->show();  // 포인터 p를 통해 show() 메서드 호출
  1. 포인터 간접 참조: 포인터를 간접 참조하여 객체를 얻고, 그 객체를 사용해 멤버 함수 호출
  (*p).show();  // 포인터 p의 간접 참조를 통해 show() 메서드 호출
  1. 객체 참조: 객체에 대한 참조 사용.
  Oval& ref = a;
  ref.show();  // 참조 ref를 통해 show() 메서드 호출



2. 포인터와 참조의 차이

참조는 직접적으로 주소 연산을 할 수 없다.

    int arr[5] = {1, 2, 3, 4, 5};

    '1. 참조를 사용한 접근'
    int& ref = arr[0];
    ref++;    // ref는 단순히 arr[0]에 대한 참조이므로, 이는 arr[0]의 값을 증가시킴
    ref+1;    // 이 경우는 단순히 arr[0]의 값에 1을 더하는 것과 같음 (값 자체의 연산)

    '2. 포인터를 사용한 접근'
    int* ptr = &arr[0];
    ptr++;     // ptr은 이제 arr[1]을 가리킴 (주소의 증가)

    '3. 포인터를 사용하여 배열의 다른 요소로 이동'
    for (int i = 0; i < 5; i++) {
        std::cout << *(ptr + i) << " "; // 출력: 2 3 4 5 (ptr이 arr[1]을 가리키고 시작하므로)
    }

1) this*this의 차이

  • this: 객체 자신의 주소를 가리키는 포인터 (ClassName*)
  • *this: this 포인터가 가리키는 객체 자체 (ClassName&)

포인터 p는 주소값이고, *p는 포인터를 역참조하여 그 객체 또는 변수 등을 뜻함.
int x = 3;
int* p = &x;	//p = 논리주소, *p = 3

this의 제약 사항

  1. 멤버함수가 아닌 함수에서 this 사용 불가
  2. static 멤버 함수에서 this 사용 불가.
    why? 객체가 생기기 전에 static 함수 호출이 있을 수 있기 때문에

2) return this;return *this;의 차이

  • return this;: 현재 객체 자신의 주소를 반환 (반환 타입: ClassName*) 주로 포인터로 객체를 반환할 때 사용
  • return *this;: 현재 객체 자신을 반환 (반환 타입: ClassName&) 주로 메서드 체이닝이나 객체의 참조를 반환할 때 사용

클래스 정의 예제 코드

class MyClass {
public:
    MyClass(int value) : value(value) {}

    MyClass* getThis() {
        return this;  // 객체 자신의 주소를 반환
    }

    MyClass& getReference() {
        return *this;  // 객체 자신을 반환
    }
};

메서드 체이닝 예제 코드

#include <iostream>

class MyClass {
public:
    MyClass& setValue(int value) {
        this->value = value;
        return *this;  // 객체 자신을 반환하여 메서드 체이닝을 가능하게 함
    }

    void show() const {
        std::cout << "Value: " << value << std::endl;
    }

private:
    int value;
};

int main() {
    MyClass obj;
    obj.setValue(10).setValue(20).setValue(30);  // 메서드 체이닝
    obj.show();  // 출력: Value: 30

    return 0;
}

이 예제에서 setValue 메서드는 객체 자신을 반환함으로써 연속적인 메서드 호출(메서드 체이닝)을 가능하게 한다. return *this;를 사용하면 객체의 참조를 반환하여 이러한 패턴을 구현할 수 있다.



3. 객체 배열 초기화

#include <iostream>
using namespace std;

class Oval {
    int width, height;
public:
    Oval() : width(1), height(1) {}           // 기본 생성자
    Oval(int w, int h) : width(w), height(h) {} // 매개변수가 있는 생성자
    void show() {
        cout << "width = " << width << ", height = " << height << endl;
    }
};

int main() {
    '1. 기본 생성자를 사용한 초기화'
    Oval arr1[3];
    for (int i = 0; i < 3; ++i) arr1[i].show();

    '2. 매개변수가 있는 생성자를 사용한 초기화'
    Oval arr2[3] = {Oval(1, 2), Oval(3, 4), Oval(5, 6)};
    for (int i = 0; i < 3; ++i) arr2[i].show();

    '3. 복사 생성자를 사용한 초기화'
    Oval obj(7, 8);
    Oval arr3[3] = {obj, obj, obj};
    for (int i = 0; i < 3; ++i) arr3[i].show();

    '4. 동적 할당과 기본 생성자를 사용한 초기화'
    Oval* arr4 = new Oval[3];
    for (int i = 0; i < 3; ++i) arr4[i].show();
    delete[] arr4;

    '5. 동적 할당과 매개변수가 있는 생성자를 사용한 초기화'
    Oval* arr5 = new Oval[3]{Oval(1, 2), Oval(3, 4), Oval(5, 6)};
    for (int i = 0; i < 3; ++i) arr5[i].show();
    delete[] arr5;

    return 0;
}

정적 할당과 동적할당

항목스택 할당힙 할당
코드 예시Oval arr[3];Oval* arr = new Oval[3];
int *p = x;int *p = new int(3); //힙에 동적으로 int 값 3 할당
사용 용도비교적 작은 크기의 고정 배열큰 배열 또는 동적으로 크기를 결정해야 할 때
메모리 관리자동 해제수동 해제 (delete[] arr;)
할당 크기 제한제한 있음 (스택 크기 제한)제한 적음 (힙은 더 큰 메모리 할당 가능)
할당/해제 속도빠름상대적으로 느림
수명함수 또는 블록이 끝나면 자동 해제명시적으로 해제 전까지 유지



문자열 string



4-12

키보드에서 원의 개수를 입력받고, 그 개수만큼 원의 이름과 반지름을 입력받는 프로그램.

main.cpp

#include "Circle.h"
#include "CircleManager.h"
#include <iostream>
using namespace std;

int main() {
    cout << "원의 개수 : ";
    int n;
    cin >> n;

    CircleManager* man = new CircleManager(n);
    man->run();
    
    delete man;
}

Circl.h 헤더 파일

#ifndef CIRCLE_H
#define CIRCLE_H

#include <string>
using namespace std;        //std::string을 사용하기 위해 미리 선언. string은 std namespace에 존재. 

class Circle {
    string name;            //string() 생성자 호출
    int radius;

public:
    void setCircle(string name, int radius);    //Circle 객체의 이름과 반지름을 설정하는 함수
    double getArea();                           
    string getName() { return name; }          //inline으로 정의.                           
};

#endif

CircleManager.h 헤더 파일

#ifndef CIRCLEMANAGER_H
#define CIRCLEMANAGER_H
#include <string>
using namespace std;

class Circle;               //#include "Circle.h"와 동일

class CircleManager {
    Circle* p;
    int size;
    void searchByName(string name);    //교재랑 다름!!
    void searchByArea(int minArea);
public:
    CircleManager(int size);
    ~CircleManager();           //왜 소멸자가 필요한가? 동적할당한 메모리를 해제하기 위해. 항상 동적할당한 메모리는 해제해야 한다.
    void run();
};

#endif

Circle.cpp

#include "Circle.h"
#include <iostream>
using namespace std;

void Circle::setCircle(string name, int radius) {
    this->name = name;
    this->radius = radius;
}

double Circle::getArea(){
    return 3.14*radius*radius;
}

CircleManager.cpp

#include "CircleManager.h"
#include "Circle.h"
#include <iostream>
using namespace std;

CircleManager::CircleManager(int size) {
    p = new Circle[size];
    this->size = size;

    for(int i = 0; i < size; i++) {
        string name;
        int radius;
        cout << "원 " << i+1 << "의 이름과 반지름 >> ";
        cin >> name >> radius;
        p[i].setCircle(name, radius);
    }
};

void CircleManager::run() {
    cout << "검색하고자 하는 원의 이름 >> ";
    string name;
    cin >> name;
    searchByName(name);

    cout << "최소 면적을 정수로 입력하세요 >> ";
    int minArea;
    cin >> minArea;
    cout << minArea << "보다 큰 원을 검색합니다." << endl;
    searchByArea(minArea);

    cout << "계속 >> (yes) >> ";
    string res;
    cin >> res;
    if (res != "yes") {
        run();
    }
}

CircleManager::~CircleManager() {
    delete[] p;
}

void CircleManager::searchByName(string name) {
    // cout << "검색하고자 하는 원의 이름 >> ";
    // string name;
    // cin >> name;

    bool found = false;

        for(int i = 0; i < size; i++) {
            if(p[i].getName() == name) {        //발견
                cout << name << "의 면적은 " << p[i].getArea() << endl;
                found = true;
                break;
            }
        }
        if(found == false)
            cout << name << "인 원을 찾을 수 없습니다." << endl;
    
}

void CircleManager::searchByArea(int minArea) {
    
    for (int i=0; i<size; i++) {
        if (p[i].getArea() > minArea)       //발견
            cout << p[i].getName() << "의 면적은 " << p[i].getArea() << ", ";
    }
}
profile
주니어 개발자

0개의 댓글