C++ 기초플러스 (12. 클래스와 동적메모리 대입)

2024. 9. 26. 13:54·LAB/C++
static 클래스 멤버

 

static  클래스 멤버는 그 클래스의 모든 객체가 공유한다.

객체 생성과 관계없이 존재하고 접근할 수 있습니다.

 

static 멤버, 사용하는 이유

  • 객체 간 데이터 공유: 모든 객체가 공통으로 사용해야 하는 데이터를 저장하고 관리하는 데 유용합니다. 예를 들어, 프로그램에서 생성된 객체의 총 개수를 세거나, 모든 객체가 접근해야 하는 설정 정보를 저장할 때 사용할 수 있습니다.
  • 객체 생성 없이 접근 가능: static 멤버는 객체를 생성하지 않고도 클래스 이름을 통해 직접 접근할 수 있습니다. 이는 객체 생성 없이도 특정 기능을 수행해야 할 때 유용합니다.
  • 메모리 효율성: static 멤버 변수는 모든 객체가 공유하기 때문에 각 객체마다 별도의 메모리를 할당하지 않아도 됩니다. 이는 메모리 사용량을 줄이는 데 도움이 됩니다.
#include<iostream>


class Counter {
public:
    static int count; // static 멤버 변수 선언

    Counter() {
        count++; 
    }
};

int Counter::count = 0; // static 멤버 변수 정의

int main() {
    Counter c1, c2, c3;

    std::cout << "생성된 객체 수: " << Counter::count << std::endl; // static 멤버 변수 접근

    return 0;
}

  1. Counter 클래스는 count라는 static 멤버 변수를 가지며, 생성자에서 count 값을 1씩 증가시킵니다.
  2. main 함수에서 Counter 객체를 3개 생성합니다.
  3. Counter::count를 통해 static 멤버 변수에 접근하여 생성된 객체 수를 출력합니다.

static 멤버, 주의할 점

  • 초기화: static 멤버 변수는 클래스 외부에서 반드시 초기화해야 합니다.
  • 접근 범위: static 멤버는 접근 지정자(public, private, protected)에 따라 접근 범위가 제한될 수 있습니다.
  • 상속: static 멤버는 상속되지만, 파생 클래스에서 재정의할 수 없습니다.

** static 데이터 멤버는 클래스 선언 안에서 선언되지만, 메서드 파일에서 초기화된다. 그 static 클래스 멤버가 어느 클래스에 속하는지 나타내기 위해서 초기화할 때 사용 범위 결정 연산자(::)를 사용한다. 그러나 static 멤버가 정수형이나 열거형의 const이면 클래스 선언자체에서 초기화할 수 있다.

 

동적 메모리 와 클래스

 

사용할 메모리의 크기를 컴파일할 때 결정하지 않고 프로그램을 실행할 때 상황에 따라 융퉁성 있게 결정하는 것이다.

다시말해, 메모리 사용을 기억 공간들의 확정된 규칙에 따르지 않고 프로그램의 요구에 맞출 수 있다.

메모리를 동적으로 제어하기 위해서, new와 delete 연산자를 사용한다.

 

▶▶▶ new와 delete 연산자

new 연산자

** 클래스 안에서 접근제어로 생성자가 만들어지고, 클래스 객체생성 시 new로 메모리 할당한다. 

** new연산 시 힙메모리에 접근하여 저장하기 위해서는 *(애스터리스크)를 사용하여 포인터 변수에 담아 저장한다.

[기능]

● 힙 메모리에 동적으로 메모리를 할당합니다.

● 객체의 경우 생성자를 자동으로 호출합니다.

 

[사용법]

int형 변수의 할당   		  int * ptr1 = new int;
double형 변수의 할당   		  double * ptr2 = new double;
길이가 3인 int형 배열의 할당   	  int * arr1 = new int[3];
길이가 7인 double형 배열의 할당     double * arr2 = new double[7];

 

[특징]

● 할당 실패 시 std::bad_alloc 예외를 던집니다.
● 초기화도 동시에 수행할 수 있습니다: int* p = new int(5);

 

delete 연산자

** 클래스 안에서 접근제어로 파괴자가 만들어지고, 클래스 객체사용 후  delete로 메모리 할당을 해제한다. 

[기능]

● new로 할당된 메모리를 해제합니다.
● 객체의 경우 소멸자를 자동으로 호출합니다.

 

[사용법]

앞서 할당한 int형 변수의 소멸 	delete ptr1;
앞서 할당한 double형 변수의 소멸 	delete ptr2;
앞서 할당한 int형 배열의 소멸 	delete []arr1;
앞서 할당한 double형 배열의 소멸 	delete []arr2;

**생성자에서 new []형식을 사용했다면 파괴자에서도 delete []형식을 사용해야한다.

 

[특징]

● 배열을 해제할 때는 반드시 delete[] 형식을 사용해야 합니다.
● null 포인터에 대해 안전하게 사용할 수 있습니다.

 

▶▶▶ 생성자와 소멸자

  • 생성자: 객체가 생성될 때 자동으로 호출되는 특별한 멤버 함수입니다. 객체의 멤버 변수를 초기화하거나 필요한 자원을 할당하는 등의 작업을 수행합니다.
  • 소멸자: 객체가 소멸될 때 자동으로 호출되는 특별한 멤버 함수입니다. 객체가 사용하던 자원을 해제하거나 정리하는 등의 작업을 수행합니다.

디폴트 생성자:

  • 매개변수가 없으며, 명시적으로 초기화하지 않고 객체를 생성할 때 사용한다.
  • 사용자가 어떠한 생성자도 제공하지 않으면, 사용자를 대신하여 컴파일러가 디폴트 생성자를 정의한다.
  • 그렇지 않을 경우에는 사용자 자신의 디폴트 생성자를 제공해야 한다. 사용자가 제공하는 생성자는 어떠한 매개변수도 사용하지 않거나, 모든 매개변수에 디폴트 값을 사용해야 한다.
Klunk :: Klunk() {} // 암시적 디폴트 생성자
Klunk lunk; //디폴트 생성자를 호출한다
Klunk :: Klunk()  // 명시적 디폴트 생성자
{
	klunk_ct = 0; 
}
class Dog {
public:
    Dog(string name) {  // 생성자
        cout << name << " 강아지가 태어났어요!" << endl;
        this->name = name;
        collar = new string("빨간색 목줄");  // new로 메모리 할당
    }

    ~Dog() {  // 소멸자
        cout << name << " 강아지가 무지개 다리를 건넜어요." << endl;
        delete collar;  // delete로 메모리 해제
    }

private:
    string name;
    string* collar;
};

int main() {
    Dog* myDog = new Dog("멍멍이");  // 새 강아지 객체 생성
    // ... 강아지와 놀기 ...
    delete myDog;  // 강아지 객체 삭제
    return 0;
}
  1. 'new Dog("멍멍이")'는 새 강아지 객체를 만들고 생성자를 호출합니다.
  2. 생성자에서는 강아지 이름을 설정하고 목줄을 만듭니다 (메모리 할당).
  3. delete myDog를 하면 소멸자가 호출됩니다.
  4. 소멸자에서는 목줄을 제거합니다 (메모리 해제).
=> 이렇게 new, delete, 생성자, 소멸자를 사용하면 객체의 생성부터 소멸까지 깔끔하게 관리할 수 있습니다.
 
#include <iostream>
#include <string>

class Person {
public:
    std::string name;
    int age;

    // 생성자
    Person(const std::string& n, int a) : name(n), age(a) {
        std::cout << "Person 객체 생성: " << name << ", " << age << "세\n";
    }

    // 파괴자
    ~Person() {
        std::cout << "Person 객체 소멸: " << name << ", " << age << "세\n";
    }
};

int main() {
    // new를 사용하여 Person 객체 동적 할당
    Person* personPtr = new Person("홍길동", 20);

    // personPtr을 통해 객체의 멤버 변수 접근 및 수정
    std::cout << personPtr->name << "의 나이는 " << personPtr->age << "세입니다.\n";
    personPtr->age = 21; 
    std::cout << personPtr->name << "의 나이는 " << personPtr->age << "세입니다.\n";

    // delete를 사용하여 Person 객체 메모리 해제
    delete personPtr; 

    return 0;
}

 

 

** 실행 순서 : main( )시작 -> 객체생성 (new 동적할당) -> 멤버변수 할당 -> 객체소멸 (delete 메모리해제) -> main( )종료

 

 

복사 생성자

 

▶▶▶ 얕은 복사 와 깊은 복사

 

[얕은 복사]

원본 객체와 복사된 객체는 같은 메모리 공간을 공유하기 때문에, 한 객체의 값을 변경하면 다른 객체에도 영향을 미칩니다.

 

얕은 복사의 장점과 단점

  • 장점:
    • 복사 속도가 빠릅니다. (단순히 값만 복사하기 때문)
    • 메모리 사용량이 적습니다. (메모리 공간을 공유하기 때문)
  • 단점:
    • 원본 객체와 복사된 객체가 메모리 공간을 공유하므로, 한 객체의 변경이 다른 객체에 영향을 준다.
    • 동적 할당된 메모리를 가리키는 포인터 멤버 변수가 있을 경우, 메모리 해제 시 문제가 발생할 수 있다.

예를들어,

얕은 복사는 객체의 멤버 변수들이 모두 기본 자료형(int, float, double 등)이거나, 객체 간에 데이터 공유가 필요한 경우에 사용할 수 있습니다. 하지만 동적으로 할당된 메모리를 가리키는 포인터 멤버 변수가 있을 경우에는 얕은 복사를 사용하면 안 된다.

#include <iostream>
#include <string>

class MyClass {
public:
    int* data;

    MyClass(int value) {
        data = new int(value); 
    }

    // 복사 생성자 (기본 얕은 복사 수행)
    MyClass(const MyClass& other) {
        data = other.data; // 포인터만 복사, 메모리 공간은 공유
    }

    ~MyClass() {
        delete data;
    }
};

int main() {
    MyClass obj1(10);
    MyClass obj2 = obj1; // 얕은 복사 발생

    *obj1.data = 20; // obj1의 data 값 변경

    std::cout << "obj1.data: " << *obj1.data << std::endl; // 20 출력
    std::cout << "obj2.data: " << *obj2.data << std::endl; // 20 출력 (obj1의 변경에 영향 받음)

    return 0; 
}

 

 

[깊은 복사]

깊은 복사에서도 원본 객체가 가리키는 메모리 공간을 복제하여 새로운 메모리 공간을 할당하기 때문에, 원본 객체와 복사된 객체는 서로 독립적입니다.

 

깊은 복사의 장점과 단점

  • 장점:
    • 원본 객체와 복사된 객체가 서로 독립적이므로, 한 객체의 변경이 다른 객체에 영향을 미치지 않습니다.
    • 동적 할당된 메모리를 가리키는 포인터 멤버 변수가 있어도 안전하게 복사할 수 있습니다.
  • 단점:
    • 복사 속도가 느립니다. (메모리 공간을 복제해야 하기 때문)
    • 메모리 사용량이 많습니다. (새로운 메모리 공간을 할당해야 하기 때문)

예를들어,

깊은 복사는 객체 간에 독립성을 유지해야 하거나, 동적으로 할당된 메모리를 가리키는 포인터 멤버 변수가 있을 때 사용해야 합니다.

 

#include <iostream>
#include <string>

class MyClass {
public:
    int* data;

    MyClass(int value) {
        data = new int(value); 
    }

    // 복사 생성자 (깊은 복사 수행)
    MyClass(const MyClass& other) {
        data = new int(*other.data); // 새로운 메모리 공간 할당 후 값 복사
    }

    ~MyClass() {
        delete data;
    }
};

int main() {
    MyClass obj1(10);
    MyClass obj2 = obj1; // 깊은 복사 발생

    *obj1.data = 20; // obj1의 data 값 변경

    std::cout << "obj1.data: " << *obj1.data << std::endl; // 20 출력
    std::cout << "obj2.data: " << *obj2.data << std::endl; // 10 출력 (obj1의 변경에 영향 받지 않음)

    return 0; 
}

 

 

복사 생성자

 

[복사 생성자 공식]  

MyClass(const MyClass& other) : data(other.data) {}

// 클래스 객체에 대한 const는 참조를 매개변수로 사용한다.
 // 복사 생성자 (기본 얕은 복사 수행)
MyClass(const MyClass& other) {
    data = other.data; // 포인터만 복사, 메모리 공간은 공유
}

MyClass obj2 = obj1; // 얕은 복사 발생
*obj1.data = 20; // obj1의 data 값 변경

 // 복사 생성자 (깊은 복사 수행)
MyClass(const MyClass& other) {
    data = new int(*other.data); // 새로운 메모리 공간 할당 후 값 복사
}

MyClass obj2 = obj1; // 깊은 복사 발생

*obj1.data = 20; // obj1의 data 값 변경

 

=>  객체를 복제하여 동일한 값을 가진 새로운 객체를 생성할 때 사용된다.

=> 함수의 매개변수나 반환 값으로 객체를 전달할 때, 복사 생성자를 통해 객체를 복사하여 전달할 수 있습니다.

이를 통해 원본 객체의 값이 변경 되는 것을 방지하고 안전하게 데이터를 전달할 수 있습니다.

 

 

비교멤버

 

비교 멤버 함수는 객체의 특정 속성을 기준으로 객체들을 비교하고, 그 결과를 반환하여 객체 정렬, 검색 등 다양한 작업에 활용될 수 있습니다.

비교 멤버 함수는 객체의 특정 속성을 기준으로 두 객체를 비교하고, 그 결과를 true 또는 false로 반환하는 특별한 멤버 함수입니다.

 

  • 객체 정렬: 객체들을 특정 기준으로 오름차순 또는 내림차순으로 정렬할 수 있습니다. 예를 들어, 학생들을 성적순으로 정렬하거나, 상품들을 가격순으로 정렬할 수 있죠.
  • 객체 검색: 특정 조건을 만족하는 객체를 빠르게 찾을 수 있습니다. 예를 들어, 특정 이름을 가진 학생을 찾거나, 특정 가격 범위에 속하는 상품들을 검색할 수 있습니다.
  • 중복 제거: 객체들을 비교하여 중복된 객체를 제거할 수 있습니다. 예를 들어, 주소록에서 중복된 연락처를 제거하거나, 데이터베이스에서 중복된 레코드를 삭제할 수 있습니다.
class Student {
public:
    int score;

    bool operator<(const Student& other) const { // 비교 멤버 함수 정의
        return score < other.score; 
    }
};

// Student 클래스에 operator< 라는 비교함수를 정의하여 이 함수는 현재 객체의 score가 다른 객체의
// score보다 작은지 비교하고, 그 결과를 true 또는 false로 반환합니다.

Student student1 { 90 };
Student student2 { 85 };

if (student1 < student2) {
    std::cout << "student1의 점수가 더 낮습니다." << std::endl;
} else {
    std::cout << "student2의 점수가 더 낮습니다." << std::endl;
}

// 객체를 student1 , student2를 생성하고 초기값 설정
// student1 < student2는 student1.operator<(student2)를 호출하는 것과 같습니다.

 

▶ 문자열 비교 멤버함수

bool operator<(const String & st1, const String & st2)
{
	if(std::strcmp(st1.str,st2.str) < 0)
    {
    	return true;
    }
    else
    {
    	return false;
    }
}

///////////////////////////////////////////////////

bool operator<(const String & st1, const String st2)
{
	return (std::strcmp(st1.str,st2.str) < 0);
}
// 내장된 < 연산자가 이미 bool형의 값을 리턴하고 있기 때문에 간결하게 가능하다

///////////////////////////////////////////////////
bool operator>(const String & st1, const String st2)
{
	return st2 < st1;
}
bool operator==(const String & st1, const String st2)
{
	return (std::strcmp(st1.str,st2.str) == 0);
}

1. > 연산자를 < 연산자로 표현하고 있다. 그것은 인라인 함수로 만들기 좋은 예이다.
2. 비교함수들을 프렌드로 만들면, String 객체와 보통의 C스타일의 문자열 비교를 할 수 있다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'LAB > C++' 카테고리의 다른 글

C++ 기초플러스(15.3 예외처리) / C++로 Mariadb 연결 및 DB Table생성  (2) 2024.09.28
C++ 기초플러스 (13. 클래스의 상속)  (1) 2024.09.27
C++ 기초플러스 (08. 함수의 활용 - 10. 객체와 클래스)  (0) 2024.09.25
C++ 기초플러스 (05. 루프와 관계 표현식 - 07. 함수 - C++의 프로그래밍 모듈)  (0) 2024.09.24
C++ 기초플러스 (02. C++ 시작하기 - 04. 복합데이터형)  (0) 2024.09.23
'LAB/C++' 카테고리의 다른 글
  • C++ 기초플러스(15.3 예외처리) / C++로 Mariadb 연결 및 DB Table생성
  • C++ 기초플러스 (13. 클래스의 상속)
  • C++ 기초플러스 (08. 함수의 활용 - 10. 객체와 클래스)
  • C++ 기초플러스 (05. 루프와 관계 표현식 - 07. 함수 - C++의 프로그래밍 모듈)
it-lab-0130
it-lab-0130
it-lab-0130 님의 블로그 입니다.
  • it-lab-0130
    빠냐냐~
    it-lab-0130
  • 전체
    오늘
    어제
    • 분류 전체보기 (69)
      • 개인 (2)
        • 개발TIP Program (2)
      • LAB (47)
        • Python (5)
        • C (13)
        • TCP_IP (7)
        • C++ (9)
        • QT_C++ (4)
        • C# (9)
      • Project (0)
        • 오류 모음 (0)
        • Python (0)
        • C 와 TCP_IP소켓 (0)
        • C++ (0)
      • Study_ (17)
        • 예제_문제풀이 (10)
        • 학습일지 (7)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • Python
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
it-lab-0130
C++ 기초플러스 (12. 클래스와 동적메모리 대입)
상단으로

티스토리툴바