Unreal 개발 본 캠프 19일차

HappyCircle·2025년 12월 23일

Unreal 개발

목록 보기
36/163

오늘 학습 진행 내용

C++ 레벨 테스트 대비 개념 정리

/*
Call By Value
값 복사의 경우 원본은 변경 되지 않고 복사본만 값이 바뀜
*/
void changeValue(int x){
    x =999;
}
int num = 10;
changeValue(num);

/*
Call By Pointer
포인터 변수의 경우 해당 주소 위치 변경으로 적용되서 원본 값 변경됨
*/
void changeValue(int* ptr){
    *ptr = 999; //원본 값 변경, 포인터 변수에 * 붙이면 값 가리키는 형태
}
int num =10;
changeValue(&num); //값 변수에는 & 앞에 붙이면 포인터 주소 가리키는 형태

/*
Call By Reference
참조의 경우 원본 변수 그대로 가져와서 적용하기 때문에 원본 값 변경됨
*/
void changeValue(int& ref){
    ref = 999; //원본 변수 그대로 가져오는 형태로 변수 그대로 사용
}
int num = 10;
changeValue(num); //그냥 변수만 전달하면 됨

/*
배열을 포인터처럼 접근
*/
int arr[3] = {10,20,30};
int* p = arr; // 포인터 변수 p로 arr 지정하면 arr[0] 해당 값 위치의 포인터 주소 저장
p =  arr[0]의 주소
p+1 = arr[1]의 주소

*p 는 arr[0]의 실제 값
*(p+1)은 arr[1]의 실제 값

/*
Swap
*/
void SwapPtr(int* a , int* b){
    int temp = *a; //temp에 a 포인터 주소 위치의 값 저장
    *a = *b; //a 포인터 주소의 값을 b 포인터 주소의 값 저장
    *b = temp; // b 포인터 주소의 값에 temp 저장
}

/*배열 Swap*/
void SwapRef(int& a, int& b){
    int temp = a;
    a = b;
    b = temp;
}
int size = sizeof(arr)/sizeof(arr[0]); //배열의 칸 갯수 
if(arr[i]>arr[i+1]){ //바로 앞 값 비교해서 큰 값을 뒤로 보내기 위해 조건 검사
    SwapRef(arr[i],arr[i+1]);
} 

/*
Class
*/
class Player{
    public: //외부 접근 가능
        Player(int hp, int mp){} //생성자 형태 1 
        Player(int hp, int mp) : hp(hp), hp(mp){} // 생성자 형태 2
        Player(){}
        Player(){
            hp =50;
            mp=50;
        }
    private: //내부 접근만 가능
        int hp;
        int mp;
    protected: //파생 클래스에서도 사용 가능, 외부 접근은 막힘
}

/*
객체지향 프로그래밍
override, overload 활용
*/
class Animal{
    public:
        Animal(){};
        virtual void speak() const{} //가상 함수로 선언된 것들 파생 클래스에서 재정의 가능
        virtual void speak()=0; //순수 가상 함수는 파생 클래스에서 재정의 반드시하게 설정
           
}
class Dog : public Animal{
    Dog(){}; //생성자의 경우 Dog로 생성될 때 부모 클래스의 생성자 실행된 후 파생 클래스의 생성자 실행
    void speak() const override{} //오버라이드 통해서 speak 재정의
    //함수 이름은 같지만 매개 변수 내용이 다르면 선언 가능(오버로드), 상속, virtual 상관없이 가능
    void eat(string food)const {}
    void eat(string food, int count) const{}
    
}

//소멸자는 부모 포인터로 객체를 삭제할 수 있기 때문에, 실제 객체 타입에 맞는 소멸자를 호출하기 위해 virtual이 필요합니다.
class Base {
public:
    virtual ~Base() {}
};

class Derived : public Base {
public:
    ~Derived() {}
};

int main() {
    Base* p = new Derived();
    delete p;
}

/*자원 관리
댕글링 포인터 -> 가리키는 포인터 위치가 메모리가 해제된 상황
*/
int* p1 = new int(10);
int* p2 = p1;
delete p1; //p2 포인터가 현재 p1 포인터의 위치를 가리키고 있어서 메모리 삭제 시 p2 포인터 댕글링 포인터 발생
p1 = nullptr;

/*
상속
파생 클래스에서는 부모 클래스에서 정의된 기능을 모두 사용 가능해야 됨
*/
class Chracter{
    protected:
        int hp;
    public:
        Character(int hp) : hp(hp){}
        void PrintHP() const{}
}
class Warrior : public Character {
    public:
        Warrior(int hp) : Chracter(hp) {} //기본 생성자 Character(){} 가 없다면 항상 파생 클래스에서 생성자에 부모 클래스 생성자 명시적으로 호출
}
Warriror w(100); //부모 클래스 생성자도 호출됨
w.PrintHP(); //파생 클래스는 부모 클래스에서 정의된 기능 사용 가능

/*
다형성
다형성은 부모 타입의 포인터 또는 참조로 객체를 다루더라도 실제 객체의 타입에 따라 서로 다른 동작이 실행됨 (virtual -> override)
*/
class Animal{
    public:
        virtual void speak() const{
            cout<<"Hello Animal"<<endl;
        };
}
class Dog : public Animal{
    public:
        void speak() const{
            cout<<"Hello Dog"<<endl;
        };
}
class Cat : public Animal{
    public:
        void speak() const{
            cout<<"Hello Cat"<<endl;
        }
}
Animal* dog = new Dog();
Animal* cat = new Cat();
dog->speak(); //Hello Dog 출력
cat->speak(); //Hello Cat 출력

/*
스마트 포인터
*/
#include <memory> //스마트 포인터 사용 시 인클루드해서 사용
//unique_ptr 해당 메모리 단일 소유권 강제 처리, 
class Weapon{
    Weapon(int d){};
}
class Player{
    unique_ptr<Weapon> weapon; // Player 가 Weapon의 단독 소유자
    void Equip(unique_ptr<Weapon> w){
        weapon = std::move(w); //소유권 이전, 단일 소유권 강제 처리하는 unique_ptr은 소유권 이전하는 방식으로 복사 대체
    }
}
Player p;
p.Equip(make_unique<Weapon>(25)); //unique_ptr은 자료형 타입, make_unique는 unique_ptr 선언
uniuqe_ptr<Weapon> sword = make_unique<Weapon>(100);

//shared_ptr은 여러 객체가 하나의 자원을 공동으로 소유, 생명주기를 명확히 한 객체가 책임지을 수 없을 대 사용
#include <memory>
class Animal{}
class Dog : public Animal{}
class Cat : public Animal{}
shared_ptr<Animal> a1 = make_shared<Dog>(); //shared_ptr은 자료형 타입, make_shared는 shared_ptr 선언 use_count +1
{
shared_ptr<Animal> a2 = a1; //shared_ptr은 복사 가능 복사 선언 시도 use_count +1 이거는 스코프가 끝나면 삭제
}
 //관리 객체가 되는 처음 선언한 shared_ptr은 reset() 시켜줘야 삭제
 a1.reset();
 //shared_ptr은 서로 참조 되는 순환 참조 문제로 use_count 0으로 rest되지 않아서 메모리 누수 발생 문제 조심해야됨
 shared_ptr<A> a = make_shared<A>();
 shared_ptr<B> b = make_shared<B>();
 a->b = b;
 b->a = a; 

 //weak_ptr는 객체를 소유하지 않고 관찰하기만 하는 포인터(shared_ptr 순환참조 문제 해결하기 위해서)
 #include <memory>
 shared_ptr<int> sp = make_shared<int>(10);
 weak_ptr<int> wp = sp; //shared_ptr 참조 shared_ptr의 use_count 상승하지 않음
 if(auto locked = wp.lock()){ //객체가 살아있으면 유효한 shared_ptr 주소 위치의 실제 값)shared_ptr 유효한지 체크
 }
class Player {
public:
    Player(const shared_ptr<int>& data) : data(data) {} // weak_ptr<int> data = shared_ptr<int> data;

    void Print() const {
        if (auto d = data.lock()) {
            cout << *d << endl;
        } else {
            cout << "Data expired\n";
        }
    }
private:
    weak_ptr<int> data;
};
/*
얕은 복사와 깊은 복사
얕은 복사는 객체 내부의 포인터 값만 복사, 내부에 있는 개체는 같은 주소를 가리킴, 내부 값을 바꾸면 원본과 복사본이 같이 변경
깊은 복사는 객체 안에 있는 모든 객체까지 새로 복사 원본과 복사본이 완전히 독립적 내부 값 변경해도 서로 영향 없음
*/
stuct Data{
    int* value;
    Data(int v){ 
        value = new int(v);
    }
    Data(const Data& other){ //깊은 복사 (복사 생성자)
        value = new int(*other.value);
    }
    Data(int v) : value(make_unique<int>(v)){} //unique_ptr 사용
    ~Data(){
        delete value;
    }
}
Data a(10);

// 복사 생성자를 정의하지 않으면 얕은 복사가 발생
// Data b = a; // 얕은 복사 (주소만 복사)

// 현재처럼 복사 생성자를 직접 구현하면 깊은 복사
Data b = a; // 깊은 복사, Data b(a); 와 같음

// unique_ptr을 사용하면 복사가 금지되고, move를 통해 소유권 이전 가능
Data c = move(a); // 이동 생성자 호출, 원본 a의 값은 이제 소유권이 넘어감

/*
템플릿
사용자 정의 타입 
*/
template<typename T>
T Add(T a, T b) //a와 b가 동일 타입이면 해당 함수 다양한 타입 받아서 사용 가능(int, double,float 등)
{
    return a + b;
}

template <typename T1, typename T2>
auto Add(T1 a, T2 b) //a와 b의 타입이 서로 다른 걸 사용하고 싶다면 템플릿 타입을 복수로 정의
{
    return a + b;
}

/*
STL-Vector
*/
#include <algorithm>
#include <vector>
vector<int> v;  //동적 배열 컨테이너 생성 초기 크기 정의 안된 상태
v.reserve(10); //동적 배열 v의 사전 공간 확보(10칸)
v.push_back(10); //특정 값 벡터의 맨뒤부터 추가 
v.insert(v.begin()+1, 10); //특정 인덱스에 삽입 비효울적
v.erase(v.begin()+1) //특정 인덱스 위치 요소 삭제
sort(v.begin(),v.end(),compare)//comare 함수에 사전 정의 정렬 기준  따라서 vector 정렬
v.size(); //벡터 크기
v.pop_back(); //가장 맨 끝 원소 제거
v.at[0]; , v[0]; //특정 위치 인덱스 요소 접근
/*
STL-Map
*/
#include <algorithm>
#include <vector>
map<string, int> Inventory;
for(auto& it = Inventory.begin(); it!=Inventory.end(); it++){
    cout<<it->first<<" : "<<it->second<<endl; //맵 키(first), 밸류(second) 쌍 출력 
}
Inventory.insert({ "Potion", 100 }) //특정 키 값 삽입

auto it = Inventory.find("Sword"); //map 에서 특정 키값 기준 찾기
if(it!=Inventory.end()) //특정 키값 기준 발견될 경우
Inventory.erase(next(inventory.begin()),inventory.end()-1); //특정 요소 삭제
 
/*
객체 지향적 설계
응집도는 높이고 결합도는 낮추기
클래스 내부 요소들 밀접 연관성(응집도)
클래스 끼리 서로 의존하는지(결합도)
SOLID 5대원칙
SRP: 단일 책임 원칙(Single Responsibility Principle) -> 하나의 클래스는 하나의 책임만 담당
OCP : 개방 폐쇄 원칙(Open-Closed Principle) -> 시로운 기능 추가 시 신규 클래스 추가
LSP : 리스코프 치환 원칙(Listkov, Substrituion Principle) -> 자식 클래스는 부모 클래스의 대체 가능
ISP : 인터페이스 분리 원칙(Interface Segreation Principle) -> 범용적인 인터페이스 하나보다 구체적인 여러 개 인터페이스
DIP : 의존 역전 원칙(Dependency Inversion Principle) -> 상위 모듈이 하위 모듈에 직접 의존 불가
*/ 
profile
개발합시다!

0개의 댓글