C++ 포큐아카데미

추상클래스는 함수 선언은 있는데 구현이 하나라도 없으면 추상 클래스다. 왜 구현을 안해놓냐?? 자식클래스가 구현을 할 거니깐! 만약 자식 클래스가 구현을 안하면 컴파일 에러가난다! 선언만 있고 구현이 없는 함수를 순수(Pure) 함수라고 한다. 위의 Speak함수는 순수함수다. 순수 가상함수는 virtual void Speack() = 0 처럼 virtual과 = 0 을 붙여주면 된다.

 

 

 

C++ 포큐아카데미

추상 클래스를 object로 만들면 당연히 컴파일 에러가 난다. 구현체가 없는데 어떻게 객체를 만들겠나..

객체를 스택에 만들든, 힙에 만들든 무조건 에러다. 하지만 자식 클래스 Cat을 만든 다음 포인터로 캐스팅 하면 된다. 왜냐하면 가상 테이블 때문에 Cat의 Speak을 호출할 걸 알기때문!

 

 

인터페이스

C++ 포큐아카데미

C++는 인터페이스가 없다. 그래서 순수 가상 함수만으로 이루어진 클래스를 interface라고 C++ 개발자들 끼리 부르기로 했다.

  

C++ 포큐아카데미

 

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

C++ 템플릿  (0) 2020.04.21
C++ 객체 백터 Vector  (0) 2020.04.17
C++ iterator 개념  (0) 2020.04.17
C++ 정적 바인딩, 동적 바인딩  (1) 2020.04.09
C++ 멤버함수의 메모리 위치  (0) 2020.04.09
문제점

C++ 포큐아카데미

위의 코드는 문제가 있다기 보다는 엄청 귀찮다. 왜냐하면 함수 오버로딩을 이용해서 매개변수, 반환 타입별로 함수를 만들기 때문이다. Add double이면 double도 만들어줘야 한다. 정말 귀찮다. 해결방법이 뭘까??

 

해결책(템플릿)
template <typename T>
T Add(T a, T b)
{
	return a + b;
}


int main()
{
	std::cout << Add<int>(3, 10) << std::endl;
	std::cout << Add<float>(3.14f, 10.14f) << std::endl;

	return 0;
}

위 처럼 템플릿 함수 Add를 만들면 컴파일 시에 int형 함수, float형 함수를 따로따로 만들어 준다. (컴파일러 정말 감사..... 꿀 기능)

 

작동 원리

템플릿을 인스턴스화 할때마다 컴파일러가 알아서 함수를 생성해준다.

그래서 자료형 가짓수에 비례해서 exe파일이 증가한다.

 

템플릿 클래스 예제
#include <stdio.h>
#include <iostream>
#include <map>


template<typename T>
class MyArray
{
public:
	bool Add(T data);
	MyArray();

private:
	enum { MAX = 3 };
	int mSize;
	T mArray[MAX];
};


template<typename T>
bool MyArray<T>::Add(T data)
{
	if (mSize >= MAX)
	{
		return false;
	}
	mArray[mSize] = data;
	mSize++;
	return true;
}

template<typename T>
MyArray<T>::MyArray()
	: mSize(0)
{
}



template <typename T>
T Add(T a, T b)
{
	return a + b;
}


int main()
{
	MyArray<int> scores;

	scores.Add(10);
	scores.Add(20);
	scores.Add(30);
	scores.Add(40);

	return 0;
}

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

추상클래스(Abstract), 인터페이스  (0) 2020.04.27
C++ 객체 백터 Vector  (0) 2020.04.17
C++ iterator 개념  (0) 2020.04.17
C++ 정적 바인딩, 동적 바인딩  (1) 2020.04.09
C++ 멤버함수의 메모리 위치  (0) 2020.04.09
Vector 값
#include <stdio.h>
#include <iostream>
#include <vector>


class Scores
{
public:
	Scores(int score, std::string subject);
	int score;
	std::string subject;
};

Scores::Scores(int score, std::string subject)
{
	this->score = score;
	this->subject = subject;
}

int main()
{
	std::vector<Scores> scores;
	scores.reserve(3);

	scores.push_back(Scores(30, "C++"));
	scores.push_back(Scores(40, "JAVA"));
	scores.push_back(Scores(50, "Python"));

	std::vector<Scores>::iterator it = scores.begin();


	for (std::vector<Scores>::iterator iter = scores.begin(); iter != scores.end(); ++iter)
	{
		std::cout << iter->score << (*iter).subject << std::endl;
	}

}

위의 코드는 Vector에 Scores 객체를 담는 코드다. 이때 new Scores가 아니라 그냥 Scores로 객체를 만들기 때문에 값 복사가 일어난다(포인터가 아님). Scores 처럼 용량이 작은 객체는 괜찮지만 용량이 큰 객체를 이런식으로 관리하면 값 복사 문제때문에 굉장히 힘들어진다.

 

 

C++ 포큐아카데미
C++ 포큐아카데미

위의 그림은 scores 객체의 capacity를 초과해서 메모리 재할당이 일어나는 경우를 나타낸 그림이다.

이렇듯 객체를 직접 보관하면 값복사 때문에 굉장히 느려질 수 있다. 이때의 해결책은 JAVA처럼 포인터를 Vector에 저장하는 방법이다.

 

C++ 포큐아카데미

 

 

위의 그림은 scores의 사본을 만들때 발생하는 값 복사 문제다. 값으로 저장되어 있기 때문에 사본을 그대로 복사한다. 문제다 문제

 

 

Vector 포인터(JAVA 방식)
	std::vector<Scores*> scores;
	scores.reserve(3);

	scores.push_back(new Scores(30, "C++"));
	scores.push_back(new Scores(40, "JAVA"));
	scores.push_back(new Scores(50, "Python"));

	std::vector<Scores*>::iterator it = scores.begin();


	for (std::vector<Scores*>::iterator iter = scores.begin(); iter != scores.end(); ++iter)
	{
		std::cout << (*iter)->score << (*iter)->subject << std::endl;
	}

위의 코드는 new를 사용해서 포인터를 저장하는 Vector다. 해서 타입도 std::vector<Scores*>이다

 

여기서 주의해야할 점은 it는 Vector의 첫번째 인덱스의 값 즉 Socre 포인터(Score *)를 가르키는 포인터다. 그래서 값에 접근하기 위해서는 (*iter)->socre와 같이 사용해야한다.

 

C++ 포큐아카데미
C++ 포큐아카데미

위의 그림과 같이 메모리 복사가 일어나도 포인터 바이트(통상적으로 4바이트) 수만 복사하면 되니깐 아주 가볍다.

 

하지만 여기서 중요한점은 포인터기 때문에 메모리 제거를 내가 스스로 해줘야한다. clear()라고 해도 알아서 메모리 해제 해주지 않는다.

 

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

추상클래스(Abstract), 인터페이스  (0) 2020.04.27
C++ 템플릿  (0) 2020.04.21
C++ iterator 개념  (0) 2020.04.17
C++ 정적 바인딩, 동적 바인딩  (1) 2020.04.09
C++ 멤버함수의 메모리 위치  (0) 2020.04.09
iterator

iterator는 C++만의 개념이 아니다. python도 있고 java에도 있다.

C++에서 iterator의 개념만 확실히 잡으면 다른 언어에서 iterator는 공짜다.

 

iterator는 한마디로 그냥 포인터다. 어떤 포인터? 배열의 요소를 가르키는 포인터

C++ 표준 라이브러리 Vector에서 begin 함수는 배열의 처음 주소를 반환하는 함수다.

 

그러니깐 scores.begin()이라고 하면 scores Vector 배열의 첫번째 주소를 반환해준다 

iter = scores.begin()에서 iter는 첫번째 주소를 가지고 있는 포인터니깐 *iter라고 하면 그 값을 참조해서 가져온다.

 

참고로 주의해야 할 점은 end함수는 vector의 마지막 요소 바로 뒤 그러니깐 쓸모없는 값을 가르키는 주소를 반환해준다! 그래야 for loop 돌릴때 편하기 때문!(조건 iter != socres.end())

#include <stdio.h>
#include <iostream>
#include <vector>

int main()
{
	std::vector<int> scores;
	scores.reserve(3);

	scores.push_back(10);
	scores.push_back(20);
	scores.push_back(30);


	for (std::vector<int>::iterator iter = scores.begin(); iter != scores.end(); ++iter)
	{
		std::cout << *iter << std::endl;
	}

}

 

iterator 그림

포큐아카데미 C++

 

 

iterator insert, delete
	std::vector<int>::iterator it = scores.begin();
	scores.insert(it, 90);

위의 코드에서 it는 socres의 첫번째 배열 주소를 가리키는 포인터다.

그 상태에서 insert를 하게되면 첫번째 주소에 90을 넣으라는 코드다.

 

그 다음 index에 insert 하고 싶으면 it++ 연산자를 이용해서 주소를 (+) 하면된다

 

삭제도 간단하다 insert 대신 erase를 사용하면 된다

 

insert, delete를 하면 연속된 배열이기 때문에 어쩔수없이 메모리 복사가 및 재할당이 일어난다.

위의 코드처럼 첫번째 요소에 90을 넣을때 모든 배열 요소를 한칸씩 뒤로 미루는 우로밀착을 한뒤에 90을 넣어야 한다(복사)

 

재할당은 현재 사용중인 배열의 capacity를 초과했을때 메모리를 다시 할당하는(malloc)을 해줘야한다. 세상에 공짜는 없는듯하다

 

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

C++ 템플릿  (0) 2020.04.21
C++ 객체 백터 Vector  (0) 2020.04.17
C++ 정적 바인딩, 동적 바인딩  (1) 2020.04.09
C++ 멤버함수의 메모리 위치  (0) 2020.04.09
C++ 객체 생성  (0) 2020.04.02
C++의 기본 바인딩은 정적 바인딩이다.

C++ 포큐아카데미

Animal을 상속받아 만든 Cat 클래스가 있다. Cat 클래스에 Speak 메소드를 오버라이딩 했다.

두개의 오브젝트를 만드는데 타입을 하나는 Cat으로 하나는 부모클래스인 Animal로 했다. 그때 speak()메소드를 호출하면 누가 나올까?

 

JAVA는 무조건 오버라이딩된 자식 메소드가 나오고 C++는 무늬 즉 타입(포인터)따라 간다.

JAVA의 기본 바인딩은 동적 바인딩이고 C++의 기본 바인딩은 정적 바인딩이다.

 

정적 바인딩

C++ 포큐아카데미

위의 그림에서 Cat 오브젝트를 만들고 타입도 Cat*다. 그래서 힙에 할당한 모든 메모리를 그냥 쓰면된다. 전혀 문제가 안됨

 

C++ 포큐아카데미

하지만 위의 그림은 Cat 오브젝트를 만들었지만 타입은 부모 클래스 즉 Animal이다. 이럴때 Animal의 것만 포인팅한다.

Cat의 메모리 영역에 접근하고 싶으면 Type, 포인터를 Cat*으로 변경해야 한다.

 

C++ 포큐아카데미

정적 바인딩을 그림으로 잘 표현한 예다. C++는 기본적으로 타입 따라간다. 왜냐? 정적바인딩이니깐

 

 

가상함수(동적 바인딩)

C++ 포큐아카데미

그렇다면 C++에서 자바처럼 무조건 만들어진 오브젝트 즉 자식 클래스의 메소드가 호출되려면 어떻게 해야 할까?

virtual 키워드를 붙이면 된다. virtual을 붙이면 자바처럼 동적 바인딩을 하겠다는말이다.

참고로 자바는 기본적으로 모든 것이 다 virtual이고 final을 붙이면 정적 바인딩이 된다.

 

동적 바인딩은 실행 중에 어떤 함수를 호출할지 결정되고 정적 바인딩 보다 느리다. 실행 중에 어떤 함수를 결정하기 위해 가상 테이블이 생성된다.

 

가상테이블

C++ 포큐아카데미

컴파일시에 virtual을 정의한 메소드에 한해서 가상 테이블이 생성되는데 오브젝트를 생성할 때 해당 클래스의 가상 테이블 주소가 함께 저장된다. (EX __vfptr, 0x00459bd8)

 

위의 가상테이블을 활용하면 오브젝트가 메소드를 호출할때 가상테이블로 가서 "야! Move 메소드 호출할건데 부모거말고 내걸로 호출해라! 니가 주소 알고 있지? 그 주소로 Jump해서 호출하고와라" 이런식으로 가상 테이블에 접근해서 메소드를 호출한다. 그렇기 때문에 느리다.

 

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

C++ 객체 백터 Vector  (0) 2020.04.17
C++ iterator 개념  (0) 2020.04.17
C++ 멤버함수의 메모리 위치  (0) 2020.04.09
C++ 객체 생성  (0) 2020.04.02
C++ 임시객체(이동 생성자) 중요!  (0) 2020.02.07
멤버함수

C++ 포큐아카데미

Cat 클래스는 Animal 클래스를 상속해서 만들었다.

myCat과 yourCat 오브젝트는 각각의 메모리 영역을 가진다(그림 참조)

그러면 과연 멤버함수 GetName은 어디에 존재할까?

myCat과 yourCat의 멤버변수처럼 각각 존재하는건지, 아니면 하나만 존재하는건지 헷갈린다.

 

정답은 각각 존재하지 않고 하나만 존재한다. 위치는 메모리의 코드섹션에 존재하며, 각 멤버 함수는 컴파일 시 딱 한번만 메모리에 할당된다. 그리고 저수준에서는 클래스의 멤버함수는 사실 전역함수와 같다.

C언어에서 전역함수인데 매개변수로 구조체를 받는 함수가 있다고 가정하면 C++에서 멤버함수는 전역함수에 속하고 매개변수는 구조체 대신 오브젝트의 포인터(주소)가 된다.

 

 

C++ 포큐아카데미

myCat, yourCat 오브젝트가 GetName 메소드를 실행하면 코드섹션에 0x0A16C7주소에 있는 함수를 실행하는 것을 확인할 수 있다.

그리고 GetName 메소드는 매개변수로 Cat*를 받는다. 그 포인터로 멤버변수에 접근한다는 말.. 멤버변수는 각각의 메모리공간을 가지니깐!

 

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

C++ iterator 개념  (0) 2020.04.17
C++ 정적 바인딩, 동적 바인딩  (1) 2020.04.09
C++ 객체 생성  (0) 2020.04.02
C++ 임시객체(이동 생성자) 중요!  (0) 2020.02.07
C++ 대입 연산자  (0) 2020.02.02
객체 생성
class Vector 
{
	int x;
	int y;
}

C++ 포큐아카데미

python이나 java 등 보편적인 언어는 클래스 객체 생성을 Heap 영역에 하는 반면 C++는 자유도가 높아 객체를 스택에도 생성할 수 있다. 물론 스택에 너무 큰 용량의 객체를 만들면 스택 오버플로우가 나니 조심해야 한다.

 

class Vector 
{
	int x;
	int y;
}

Vector v;

위의 예제처럼 객체를 만들면 8바이트(int 4 바이트라고 가정) 만큼 스택에 할당한다.

 

public class Vector 
{
	int x;
	int y;
}

Vector* v = new Vector();

C++ 포큐아카데미

위의 예제처럼 객체를 생성하면 스택영역의 변수 b에 Vector를 할당한 Heap영역의 메모리 첫번째 주소를 담는다.

 

 

객체 배열 생성

C++ 포큐아카데미

우측의 C++부터 보면 Heap영역에 Vector 객체 10개를 만들고(8바이트 * 10 = 80바이트) 그 첫번째 주소를 스택의 list 변수에 담는다. 그래서 list 변수의 타입은 Vector *이다.

 

반면에 Java의 경우 new Vector[10]을 해도 객체를 10개 만들지 않고 Vector 객체를 담을 수 있는 레퍼런스(주소)를 반환한다. (포인터가 4바이트라고 가정하면 4바이트 * 10 = 40바이트) 실제로 값을 담으려면 for문을 10번 돌면서 값을 생성해주면 된다.

 

 

C++ 포큐아카데미

정리하면, 힙에 Vector 10개를 바로 만드려면 Vector* list = new Vector[10]을 하면 되고, JAVA에서는 불가능하다. 왜냐하면 모든게 다 레퍼런스 즉 포인터라서.

 

C++에서 Java의 Vector 객체를 담을 수 있는 레퍼런스(주소) 10개를 반환하는 코드를 만드려면

Vector** list = new Vector*[10] 이렇게 하면된다.

 

list 타입을 설명하자면 처음의 * 포인터는 배열 그 자체 그러니깐 전체를 나타내는 것이고 두번쨰 * Vector는 각각의 배열은 Vector 객체를 가르키는 포인터를 담는다는 뜻이다. 

 

C++ 포큐아카데미

마지막으로 객체를 삭제할때다. C++에서 delete를 안해주면 큰일난다. Java처럼 자동으로 가비지컬렉터가 지워주지 않기때문이다. 반드시 삭제하도록 하자

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

C++ 정적 바인딩, 동적 바인딩  (1) 2020.04.09
C++ 멤버함수의 메모리 위치  (0) 2020.04.09
C++ 임시객체(이동 생성자) 중요!  (0) 2020.02.07
C++ 대입 연산자  (0) 2020.02.02
C++ 깊은복사 얕은복사  (0) 2020.02.02
전체코드
#include <iostream>
#include <cstring>
using namespace std;

class MyClass 
{
public:
	MyClass(const char _name[])
	{
		this->name = new char[strlen(_name) + 1];
		strcpy_s(this->name, strlen(_name) + 1, _name);
		cout << "기본 생성자 MyClass()" << this->name << endl;
	}

	MyClass(const MyClass& rhs)
	{
		this->name = new char[strlen(rhs.name) + 1];
		strcpy_s(this->name, strlen(rhs.name) +1, rhs.name);
		cout << rhs.name << "@@@@@@@@@@@복사 생성자 MyClass(const MyClass &rhs)@@@@@@@@@@" << endl;
	}

	//MyClass(MyClass&& rhs) : name(rhs.name)
	//{
	//	cout << rhs.name << "########## 이동 생성자(const MyClass&& rhs)##########" << endl;
	//  rhs.name = nullptr;
	//}

	operator string() { return this->name; }

	MyClass operator+(const MyClass& rhs)
	{
		cout << "operator+" << endl;
		MyClass temp(rhs.name);
		int thisNameLen = strlen(this->name);
		int rhsNameLen = strlen(rhs.name);
		strcat_s(temp.name, (thisNameLen + rhsNameLen + 1), this->name);
		return temp;
	}

	MyClass& operator=(const MyClass& rhs)
	{
		cout << "operator=" << endl;
		return *this;
	}

	void setName(const char _name[])
	{
		delete this->name;
		this->name = new char[strlen(_name) + 1];
		strcpy_s(this->name, strlen(_name) + 1, _name);
	}

	void printName()
	{
		cout << this->name << endl;
	}

private:
	char *name;
};


int main() 
{
	cout << "##########begin#########" << endl;
	MyClass a("aa"), b("bb"), c("cc");
	MyClass d = a + b + c;
	a.printName();
	cout << string(a) << endl;

	b.printName();
	cout << string(b) << endl;

	c.printName();
	cout << string(c) << endl;

	d.printName();
	cout << string(d) << endl;



	cout << "##########end#########" << endl;
	return 0;
}


// 출력값
##########begin#########
기본 생성자 MyClass()aa
기본 생성자 MyClass()bb
기본 생성자 MyClass()cc
operator+
기본 생성자 MyClass()bb
bbaa@@@@@@@@@@@복사 생성자 MyClass(const MyClass &rhs)@@@@@@@@@@
operator+
기본 생성자 MyClass()cc
ccbbaa@@@@@@@@@@@복사 생성자 MyClass(const MyClass &rhs)@@@@@@@@@@
aa
aa
bb
bb
cc
cc
ccbbaa
ccbbaa
##########end#########

MyClass라는 클래스를 만들고 인스턴스 a,b,c를 각각 정의했다.

이때는 당연히 기본생성자가 불린다.

파라미터 값으로 문자열을 받으니 _name[]으로 정의했다.

그런다음 name 포인터에 문자열을 담는 아주 간단한 기본 생성자이다.

 

문제는 MyClass d = a +b + c; 여기에 있다.

골때리게도 이 한줄이 이렇게 많은 생성자를 호출한다...

생성자를 호출한다는건 인스턴스가 생성된다는 뜻이고 여기에 따른 메모리, CPU Loss가 엄청나다.

 

여기서 주목해야할 부분은 바로 복사 생성자와 operator+ 더하기 연산자다. 

복사 생성자에서는 메모리 할당 문제 때문에 Deep Copy를 수행한다. (이전 글 참조)

2020/02/01 - [C++] - C++ 복사 생성자(참조형 파라미터)

 

C++ 복사 생성자(참조형 파라미터)

복사 생성자는 객체의 복사본을 생성할 때 호출되는 생성자이다. CMYData a CmyData b(a) 이처럼 원본 a의 복사본을 만들때 복사 생성자가 불린다. 문법은 클래스이름(const 클래스이름 &rhs) 처럼 사용한다. #inc..

leemoney93.tistory.com

operator + 를 살펴보자.

 

operator+
	MyClass operator+(const MyClass& rhs)
	{
		cout << "operator+" << endl;
		MyClass temp(rhs.name);
		int thisNameLen = strlen(this->name);
		int rhsNameLen = strlen(rhs.name);
		strcat_s(temp.name, (thisNameLen + rhsNameLen + 1), this->name);
		return temp;
	}
    
    a + b === a.operator+(b)

operator+에서는 할말이 아주 많은데 천천히 하나씩 해보면

1. operator+는 a+b를 할때 불리는 연산자이다.

2. a+b는 내부적으로 a.operator+(b)랑 같다

3. 그러므로 위의 코드 operator+의 rhs 값은 오른쪽 즉 b의 인스턴스의 참조이다.

4. operator+ 내부에서 temp 인스턴스를 생성하는데 b의 name값을 가지고 생성한다.

 ※ 기본 생성자 MyClass()bb 출력

5. a의 name값과 b의 name값을 합쳐서(strcat_s) MyClass instance temp를 return 한다.

 - (중요)이때 복사 생성자가 출력되는데 그 이유는 return temp를 할때 임시 객체를 생성하고 return 하기 때문이다.

MyClass(const MyClass& rhs)
{
  this->name = new char[strlen(rhs.name) + 1];
  strcpy_s(this->name, strlen(rhs.name) +1, rhs.name);
  cout << rhs.name << "@@@@@@@@@@@복사 생성자 MyClass(const MyClass &rhs)@@@@@@@@@@" << endl;
}
// 출력값
bbaa@@@@@@@@@@@복사 생성자 MyClass(const MyClass &rhs)@@@@@@@@@@

6. 위의 코드는 복사 생성자가 불릴땐데 rhs.name은 bbaa이다.

그러니깐 위의 temp instance를 기반으로 복사 생성이 이루어졌단뜻!

왜냐?? a + b를 하면 + c를 또 해야되는데(d = a + b + c) a + b의 결과 값을 가지는 임시 객체를 만들어야 하기 때문에 복사 생성이 필요하단 뜻이다. 근데 도대체 이동 생성이랑 무슨 관계인데?? 

위의 복사 생성은 Deep Copy를 하고있다. 아니 조만간 사라질놈이 왜 Deep 카피를 하냐 성능 떨어지게 그냥 포인터로 간단하게 끝내서 성능향상을 하면 되잖아! 이 논리로 나온게 바로 이동 생성이다.

MyClass(MyClass&& rhs) : name(rhs.name)
{
    cout << rhs.name << "########## 이동 생성자(const MyClass&& rhs)##########" << endl;
    rhs.name = nullptr;
}
    
// 출력값
bbaa########## 이동 생성자(const MyClass&& rhs)##########

전체코드의 주석을 풀자.

그럼 복사 생성자 대신 이동 생성자가 불린다.

이동 생성자의 rhs는 무엇일까? 바로 temp이다. 그러니깐 return temp를 할 때 temp instance를 기반으로 이동 생성자가 불렸다는 뜻. 당연하다 복사 생성자도 temp instance를 기반으로 했으니.

 

이동 생성자에서는 new로 메모리 할당받아서 Deep Copy이런거 안한다.

그냥 rhs.name(이때 rhs는 temp 임시 객체 r-value 참조)의 주소 값(포인터니깐)을 name에 할당 그러니깐 주소값만 name에 넣어주고 끝이다. 왜냐하면 조만간 사라질 임시 객체니깐! 그리고 그 값을 고스란히 return 하고 아름답게 전사한다. 이때 return 한 임시 객체 값은 + c 의 연산과정에서 또 쓰이게 된다!

 

이로써 Deep Copy(복사생성)에서 메모리 할당받고 어쩌고 저쩌고를 포인터 하나로 아주 성능 좋게 바꿨다.

 

 

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

C++ 멤버함수의 메모리 위치  (0) 2020.04.09
C++ 객체 생성  (0) 2020.04.02
C++ 대입 연산자  (0) 2020.02.02
C++ 깊은복사 얕은복사  (0) 2020.02.02
C++ 복사 생성자(참조형 파라미터)  (0) 2020.02.01

+ Recent posts