1. 자원 관리하기
1. 스택 메모리
- 메모리 영역이 크지는 않지만, 생존 영역을 벗어나면 자동으로 해제시켜준다.
- 스택 메모리의 생존 주기는 { } 안이다. 밖으로 나가면 해제된다.
- static 키워드
- 한번 선언되면, 프로그램 종료될 때 소멸된다.
- 함수 안에서 선언한 후 함수가 끝나더라도, 값이 사라지지 않는다.
- 아래 예시의 경우 11, 12, 13, ... , 20 까지 출력된다.
#include <iostream>
using namespace std;
void func()
{
static int a = 10;
a++;
cout << "a : " << a << "\n";
}
int main()
{
int n = 10;
while(n--)
func();
return 0;
}
2. 힙 메모리
- 프로그램 실행 시 동적으로 할당하는 공간은 힙 메모리를 활용하게 된다.
- new와 delete 연산자를 이용
- 스택 메모리 영역처럼 자동으로 해제되지 않기 때문에, 코드를 짤 때 조심해야 한다.
- 변수 하나의 동적 할당과 해제
void func()
{
int* ptr = new int(10);
delete ptr;
}
- 배열의 동적 할당과 해제
- 아래 예시는 사용자의 입력을 받아서 배열의 크기를 할당해주는 예시이다.
- 실제로 이런식으로 사용하는 것은 위험할 수 있다.
void func()
{
int size;
cin >> size;
if (size > 0)
{
int* arr = new int[size];
for (int i = 0; i < size; ++i)
{
arr[i] = i * 2;
}
}
}
3. Dangling Pointer
- 가리키고 있던 메모리 공간이 해제 되어 버려서, 포인터가 해지된 공간을 계속해서 가리키고 있는 상황을 말한다.
- 해지된 곳을 가리키는 것은 Segment Fault (잘못된 메모리 참조) 를 발생시킬 수 있다.
- dangling pointer를 발생시킬 수 있는 두가지 나쁜 예시
- 메모리 공간을 공유하는 두 개의 포인터 중 한 가지만 해제하는 경우(func1)
- 같은 공간의 메모리 해제를 두 번하는 경우(func2)
- func2의 경우 컴파일 이전에 warning을 준다. (초기화 되지 않은 메모리를 사용한다)
- 디버거로 실행한 경우 아래의 오류가 뜬다.
void func1()
{
int* ptr = new int(40);
int* ptr2 = ptr;
cout << "ptr adress = " << ptr << endl;
cout << "ptr2 adress = " << ptr2 << endl;
cout << *ptr << endl;
delete ptr;
cout << *ptr2 << endl; //40이 아닌 이상한 값이 출력된다.
}
void func2()
{
int* ptr = new int(30);
cout << "Value: " << *ptr << endl;
delete ptr;
delete ptr; //이 경우 컴파일러에서도 warning 을 주고, 컴파일 후 비정상적으로 종료된다.
}
4. Smart Pointer
- 위에서 언급한 힙 메모리에서 관리해야 하는 데이터들은 메모리 해제가 굉장히 중요하기 때문에 코드를 잘 관리하여 메모리 할당과 해제를 잘 해줘야 한다는 어려움이 있다.
- 이러한 어려움을 해결해 주는 것이 reference counter를 이용한 smart pointer이다.
- smart pointer는 자신을 참조하고 있는 포인터의 갯수가 0이 되는 순간 자동적으로 메모리를 해제해주는 방식으로 동작한다.
- #include <memory> 를 해서 사용할 수 있다.
- 종류
- unique_ptr
- reference counter가 최대 1인 smart pointer이다.
- 그렇기 때문에 복사 혹은 대입이 불가능하다.
- move를 통해서 소유권을 이전시켜주는 방식은 가능하다.
- shared_ptr
- reference counter가 N개가 될 수 있는 smart pointer이다.
- 현재 reference counter를 볼 수 있는 use_count()와
- 현재 포인터를 초기화하는 reset() 함수가 존재한다.
- unique_ptr
- unique_ptr 예시
#include <iostream>
#include <memory>
using namespace std;
class MyClass
{
public:
MyClass(int val) :value(val)
{
cout << "MyClass 생성: " << value << "\n";
}
~MyClass()
{
cout << "MyClass 소멸" << value << "\n";
}
const void Display()const
{
cout << "value: " << value << "\n";
}
private:
int value;
};
int main()
{
unique_ptr<MyClass> myObject = make_unique<MyClass>(10);
cout << "--------\n";
myObject->Display();
unique_ptr<MyClass> newObject = std::move(myObject);
if (myObject == nullptr)
{
cout << "empty object\n";
}
newObject->Display();
cout << "--------\n";
return 0;
}
- shared_ptr 예시
#include <iostream>
#include <memory>
using namespace std;
class MyClass
{
public:
MyClass(int val) :value(val)
{
cout << "MyClass 생성: " << value << "\n";
}
~MyClass()
{
cout << "MyClass 소멸" << value << "\n";
}
const void Display()const
{
cout << "value: " << value << "\n";
}
private:
int value;
};
int main()
{
shared_ptr<MyClass> myObject = make_shared<MyClass>(10);
cout << "--------\n";
shared_ptr<MyClass> newObject1 = myObject;
shared_ptr<MyClass> newObject2 = newObject1;
cout << "ref count : " << myObject.use_count() << "\n";
newObject1->Display();
newObject2->Display();
newObject2.reset();
cout << "ref count : " << myObject.use_count() << "\n";
cout << "--------\n";
return 0;
}
5. 얕은 복사와 깊은 복사
- 얕은 복사
- 포인터 연산에서 실제 값이 복사되는 것이 아니라 위치가 공유되는 것이다.
- 포인터 연산에서 대입연산자 주의하자
- 아래 예시처럼 대입하는 경우 위치가 공유되는 것이다!
- 위치가 공유된다는 것은 B라는 int 타입의 포인터가 만들어진 후 A 포인터가 가리키는 주소값을 가리키게 만들었다 라는 의미이다.
- 아래의 예시에서 주석처리한 곳에 ...된 곳을 보면 A와 B의 주소값이 같은 것을 볼 수 있다.
- 즉 A와 B는 같은 곳을 가리킨다는 의미이다.
#include <iostream>
using namespace std;
int main()
{
int* A = new int(30);
int* B = A;
cout << *A << " " << A << "\n"; //30, ...50
cout << *B << " " << B << "\n"; //30, ...50
delete A;
cout << *B << endl; //dangling pointer
return 0;
}
- 깊은 복사
- dangling pointer 방지하기
- 주석처리한 곳에 ... 된 곳이 주소값을 말하는데, A와 B의 주소값이 다른 것을 볼 수 있다.
- 즉, 30이라는 값을 가리키는 새로운 메모리 공간을 할당한 것
#include <iostream>
using namespace std;
int main()
{
int* A = new int(30);
int* B = new int(*A);
cout << *A << " " << A << "\n"; // 30, ...10
cout << *B << " " << B << "\n"; // 30, ...90
delete A;
cout << *B << endl; // 30
delete B;
return 0;
}
6. 숙제
- 숙제1
- 소멸자에 delete 넣어주기
- 숙제2
#include <iostream>
#include <memory>
#include <string>
using namespace std;
class Logger
{
public:
Logger()
:logCount(0)
{}
~Logger()
{
cout << "Logger instance destroyed.\n";
}
void LogInfo(string message);
void LogWarning(string message);
void LogError(string message);
void ShowTotalLogs();
private:
int logCount;
};
void Logger::LogInfo(string message)
{
logCount++;
cout << "[INFO]: " << message << "\n";
}
void Logger::LogWarning(string message)
{
logCount++;
cout << "[WARNING]: " << message << "\n";
}
void Logger::LogError(string message)
{
logCount++;
cout << "[ERROR]: " << message << "\n";
}
void Logger::ShowTotalLogs()
{
cout << "Total logs recorded: " << logCount << "\n";
}
int main()
{
unique_ptr<Logger> logger = make_unique<Logger>();
logger->LogInfo("System is starting");
logger->LogWarning("Low disk space");
logger->LogError("Unable to connect to the server");
logger->ShowTotalLogs();
return 0;
}
2. 템플릿
1. 함수 오버로딩
- 이름이 같은데, 매개변수가 다른 함수
- 매개변수의 타입이 다르거나
- 매개변수의 갯수가 다르거나
- 함수 오버로딩의 순서
- 정확한 타입 일치
- 암묵적인 타입 변환
- 오버로딩된 함수 매개변수 타입이 (int, int) 와 (float, float) 인 경우
- 우리가 해당 함수에 int float 타입 변수를 넣었다면, 두 번째로 넣은 변수인 float를 int로 형변환 하여 (int, int) 함수를 호출한다.
2. 템플릿
- 숙제
- 파라메터가 boolean 일 때 컴파일 에러 발생시켜야 하는데, 이대로 구현한 결과 에러가 발생하지 않아
- bool 타입 템플릿 특수화와 = delete; 를 통해 에러를 발생시키도록 하였다.
#include <iostream>
using namespace std;
template <typename T>
T Add(T a, T b)
{
return a + b;
}
//컴파일 에러 발생시키기 위한 처리
template<>
bool Add(bool a, bool b) = delete;
int main() {
// 정수 더하기
cout << "3 + 5 = " << Add(3, 5) << endl;
// 실수 더하기
cout << "2.5 + 4.3 = " << Add(2.5, 4.3) << endl;
// 문자열 합치기
cout << "\"Hello, \" + \"World!\" = " << Add(string("Hello, "), string("World!")) << endl;
// 아래 코드는 컴파일 에러가 발생해야 함
//cout << Add(true, false) << endl;
return 0;
}
'C++' 카테고리의 다른 글
C++ TIL day 9 (3) | 2024.12.27 |
---|---|
C++ TIL day 8 (0) | 2024.12.26 |
Smart Pointer 보충 (0) | 2024.12.24 |
C++ 상속 (0) | 2024.12.23 |
C++ TIL day 6 (0) | 2024.12.23 |