C++

[C++] CP 07, 08

Eunice99 2022. 4. 4. 04:30
  • 객체 포인터와 배열
#include <iostream>
using namespace std;

class Line {
private:
	int x1, y1;
	int x2, y2;
public:
	int* pX; 

	Line();
	Line(int _x1, int _y1, int _x2, int _y2);
	~Line(); //소멸자
	void print();
	//setter(accesor) : 데이터는 감추고, 접근할수있는 접근자
	void setPos1(int _x1, int _y1); //시작점
	void setPos2(int _x2, int _y2); //끝점
};

void Line::setPos1(int _x, int _y) {
	x1 = _x;
	y1 = _y;
}

void Line::setPos2(int _x, int _y) {
	x2 = _x;
	y2 = _y;
}

void Line::print() {
	cout << "[Line] (" << x1 << "," << y1 << ")~(" << x2 << "," << y2 << ")" << endl;
}

Line::Line() { //생성자는 void X
	x1 = 0;
	y1 = 0;
	x2 = 0;
	y2 = 0;
}

Line::Line(int _x1, int _y1, int _x2, int _y2) {
	x1 = _x1;
	y1 = _y1;
	x2 = _x2;
	y2 = _y2;
}

Line::~Line() {

}

int main() {

	Line a(1,1,10,10);
	a.print();

	int ii;
	//변수 선언 *의 의미 : 이 변수가 포인터 변수이다.
	int* ip = nullptr; //변수가 만들어지는 순간에 초기화하는게 필수 (특히나 포인터는 실수할 가능성이 높으므로 더욱더 조심), 가르킬 값이 없으면 null pointer라도! (주소값이 없다)
	ip = &ii; //포인터는 가르키고있는 그 주소에 가는것이 중요함

	//포인터 변수 자체의 *의 의미 : 그 주소에 있는 값을 의미
	(*ip) = 10; //p라는 포인터변수에 10을 넣는다.

	//포인터에 변수 타입이 따라 붙는 이유 : 주소에 더해서 그 주소에 가면 int가 있다는것까지 표현해야 해서


	//class 포인터
	Line* p = &a;
	(*p).print(); //괄호를 안치면 다른 의미가 된다. 
	a.pX = &ii;
	*a.pX = 10; //우선순위는 *와 . 중에서 .이 더 높으므로 a라는 클래스 변수에 멤버변수로 있는 pX라는 포인터의 값은 10이다 (*을 먼저보지않음), 즉 *(a.pX)로 봄

	//*p.print();는 *(p.print())와 동일. 즉, p라는 포인터자체는 접근해서 값을 열수없음. 주소를 열수있는 건 클래스변수일때만 가능함. print는 함수이므로 함수에는 *을 붙일수없음 (포인터가 아니므로)
	
	(*p).print(); //p가 가르키는 값 즉, a에 가면 print()라는 함수가있다.

	//같은의미임
	a.print(); //그냥 변수일경우에는.을 사용
	p->print(); //p라는 포인터에 가서 열면 print라는 함수가있다.

	Line arr[10]; //배열을 만들면 기본생성자가 호출됨. (따로 지정하지않는다면) 즉, 배열을 만들땐 기본생성자가 없으면 만들수가없음.
	
	//각각 만들수도 있음
	Line arr1[3] = { Line(3, 4, 5, 6), Line(1,1,1,1),Line(2, 5, 2,6) };
	for (int i = 0; i < 3; i++)
		arr[i].print();

	//2차원 배열 초기화 (뒤에서부터봄)
	Line arr2[2][3] = { { Line(3, 4, 5, 6), Line(1,1,1,1),Line(2, 5, 2,6) }, { Line(1,1,1,1), Line(2,2,2,2),Line(3,3,3,3) } };
	for (int i = 0; i < 2; i++) {
		for (int j = 0; j < 3; j++) {
			arr2[i][j].print();
		}
		cout << "=======================" << endl;
	}
		
	for (int i = 0; i < 10; i++) {
		arr[i].setPos1(i, i);
		arr[i].setPos2(3 * i, 4 * 1);
	}
	for (int i = 0; i < 10; i++)
		arr[i].print();


	//배열은 포인터로 접근 가능
	Line* q = &(arr2[1][0]); //arr[0][0]
	for (int i = 0; i < 3; i++) { //i<6 이렇게 순차적으로 해도 상관없음
		q[i].print();
	}

	return 0;
}
  • 동적 메모리 1-1
#include <iostream>
#include <cstdlib>
using namespace std;

class Line {
private:
	int x1, y1;
	int x2, y2;
public:
	Line();
	Line(int _x1, int _y1, int _x2, int _y2);
	~Line(); //소멸자
	void print();
	//setter(accesor) : 데이터는 감추고, 접근할수있는 접근자
	void setPos1(int _x1, int _y1); //시작점
	void setPos2(int _x2, int _y2); //끝점
};

void Line::setPos1(int _x, int _y) {
	x1 = _x;
	y1 = _y;
}

void Line::setPos2(int _x, int _y) {
	x2 = _x;
	y2 = _y;
}

void Line::print() {
	cout << "[Line] (" << x1 << "," << y1 << ")~(" << x2 << "," << y2 << ")" << endl;
}

Line::Line() { //생성자는 void X
	x1 = 0;
	y1 = 0;
	x2 = 0;
	y2 = 0;
}

Line::Line(int _x1, int _y1, int _x2, int _y2) {
	x1 = _x1;
	y1 = _y1;
	x2 = _x2;
	y2 = _y2;
}

Line::~Line() {

}

int main() {

	//C
	/*int* darr1 = (int*)malloc(sizeof(int) * 10); //10개의 배열 만들기, int형 포인터로 사용
	Line* darr2 = (Line*)malloc(sizeof(Line) * 10); //생성자랑 전혀 상관없음

	free(darr1);
	free(darr2);*/

	//C++
	int* darr1 = new int[10]; //int 10개를 만들어서 줄것이다라는 의미
	Line* darr2 = new Line[10];
	Line* dp = new Line(2, 3, 4, 5); //하나를 만든다는 의미

	delete[] darr1;
	delete[] darr2;
	delete dp;
	dp = nullptr; //delete를 지우고 항상 초기화해주기! (메모리를 가르치지않는 포인트는 무시해버리기때문)
	delete dp; //nullptr로 초기화하지않으면, 두번 지울수없음.

	//기본생성자가 없을때 초기화하는 방법 (장점-내가원하는 생성자를 각각의 원소를통해 만들수있다, 단점-정적배열이다.)
	Line* darr4[10]; //포인터를 10개를 만든다는 것
	//Line* darr4[size]; //정적배열이므로 이렇게 지정할수없다.

	for (int i = 0; i < 10; i++) {
		darr4[i] = new Line(2, 2, 3*i, 4*i);
	}

	for (int i = 0; i < 10; i++) {
		darr4[i]->print(); //포인터의 멤버함수를 호출한다.
	}

	for (int i = 0; i < 10; i++) {
		delete darr4[i];
	}

	//개수를 동적으로 받고싶다면? 포인터의 배열을 만드는것을 동적으로 하면된다.
	int size = 100;
	//Line* darr5[size]; 
	
	//기존 동적배열 방식에서 int 자리에 Line*를 집어넣으면됨.
	/*int a[10];
	int *a = new int[10];*/
	Line** darr5 = new Line * [size]; //포인터의 배열 사이즈가 생겨나는것. (포인터의 배열을 동적으로 n개 만드는것-더블포인터), 포인터배열로 만들었기때문에 서로다른 위치의 메모리를 참조하게됨.

	//각각의 원소 생성
	for (int i = 0; i < size; i++) {
		darr5[i] = new Line(2, 2, 3 * i, 4 * i);
	}

	for (int i = 0; i < size; i++) {
		darr5[i]->print(); //포인터의 멤버함수를 호출한다.
	}

	//각각의 원소 지우기
	for (int i = 0; i < size; i++) {
		delete darr5[i];
	}

	//배열 이름 자체도 지워줘야함. (전체배열 지우기)
	delete [] darr5;

	return 0;
}
  • this 포인터 (클래스 속에서 자기자신을 가리킴)
#include <iostream>
#include <cstdlib>
#include <cmath>
using namespace std;

class Line {
private:
	int x1, y1;
	int x2, y2;
public:
	Line();
	Line(int _x1, int _y1, int _x2, int _y2);
	~Line(); //소멸자
	void print();
	//setter(accesor) : 데이터는 감추고, 접근할수있는 접근자
	void setPos1(int _x1, int _y1); //시작점
	void setPos2(int _x2, int _y2); //끝점
	float getLength() { return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); } //선분의 길이, sqrt() : math함수(제곱함수)
	bool isLong(Line* in); //in이랑 비교해서 내가 크면 true, 아니면 false
};

void Line::setPos1(int _x, int _y) {
	x1 = _x;
	y1 = _y;
}

void Line::setPos2(int _x, int _y) {
	x2 = _x;
	y2 = _y;
}

void Line::print() {
	this->x1;
	cout << "[Line] (" << x1 << "," << y1 << ")~(" << x2 << "," << y2 << ")" << endl;
}

Line::Line() { //생성자는 void X
	x1 = 0;
	y1 = 0;
	x2 = 0;
	y2 = 0;
}

Line::Line(int _x1, int _y1, int _x2, int _y2) {
	x1 = _x1;
	y1 = _y1;
	x2 = _x2;
	y2 = _y2;
}

Line::~Line() {

}

//전역함수 (a와 b의 길이중 어느것이 더긴지 비교)
bool compare(Line *a, Line *b) { //class를 인자로 받을때는 용량이 커서 포인터로 받는다.
	if (a->getLength() > b->getLength())
		return true;
	return false;
}

bool Line::isLong(Line* in) { //멤버함수
	//코드를 중복해서 짜지않고
	/*if (getLength() > in->getLength())
		return true;
	return false;*/

	//compare 함수 이용
	if (compare(this, in) == true) //this : 나, 멤버함수에서 자기자신을 넘겨줄때 this를 사용
		return true;
	return false;

	//return compare(this, in);과 같은 의미
}

int main() {


	return 0;
}
  • 동적 메모리 1-2
#include <iostream>
using namespace std;



int main() {
	//int* arr = new int[10];  : int*가 배열을 하나 만드는것
	//int * [] : 포인터의 배열을 만들면 2차원 배열을 만들수있음.

	//int arr[10][5]와 갯수는 같지만, 메모리 구조는 완전히 다름.
	//int arr[10][5] : 5개씩 10묶음 있는것 (int 50개가 한줄로 쭉 만들어지는것, 각각 5묶음으로 생각하는것) -1차원배열과 호환됨
	int* arr[10]; //int*의 배열
	for (int i = 0; i < 10; i++) { //배열이있는데, 각각의 배열이 포인터임. 포인터가 5개씩 있는 원소를 가리킴. (5개씩 파편화되어있음) 
		arr[i] = new int[5];
	}

	for (int i = 0; i < 10; i++) {
		delete[] arr[i];
	}


	return 0;
}
  • 동적 메모리 1-3 (2차원 배열 동적할당)
#include <iostream>
using namespace std;

void func1(int arr[][5]) { //함수의 인자를 배열로 받기
	arr[1][3]; //5개씩 묶음을 한번 건너띄고 3번 건너띄고 시작되는값.
}

void func2(int** arr) {
	arr[1][3]; //두번째 포인터가 의미하는 주소에 가서 3번째 있는값을 읽어라.
}
int main() {
	int n = 10;
	int m = 5;
	int brr[10][5];
	func1(brr); //메모리구조상 1차원배열처럼 일렬로 되어있어서 가능.

	//int arr[n][m];

	int** arr = new int *[n]; //포인터의 배열을 동적으로 만들어주어야함(new로 만든것은 n개의 포인터)
	for (int i = 0; i < n; i++) { 
		arr[i] = new int[m];
	}

	//func1(arr); //메모리구조상 파편화되어있어서 받을 수 없음.
	func2(arr);

	for (int i = 0; i < n; i++) {
		delete[] arr[i];
	}

	delete[] arr;

	return 0;
}
  • String 1-1
#include <iostream>
#include <string>
using namespace std;

void func(char *str) { //문자열을 인자로 받는 함수
	str[0] = 'S';
}

void func1(const char* str) { //char에 포인터를 받을건데, 그 값을 변경시키지않을거야 (포인터가 가르키고있는 값을 변경하지않을것이다)
	//str[0] = 'S'; 바꾸려는 시도는 에러가남.
	char c = str[0]; //값을 읽으려는 시도는 괜찮음.
	c = 'S'; //읽은다음에 바꾸는건 상관없음. 자체를 바꾸는것이 나이므로
}

int main() {
	string a("sejong university"); //문자열의 literal로 입력받는 함수는 const char*로 만들어야 받을수있음.
	cout << a << endl;

	//func("software")와 func(text)의 차이
	char text[] = "software"; //(프로그램 실행 내내 변하지않음) literal이므로 컴파일링할때 이미 정해져있어서 변하지않음.
	text[0] = 'S'; //배열을 만들어서 주게되면 복사본이 만들어지는것임. software라는 문자열은 사라지지않고 복사본의 값을 변화시키는것임.
	func(text);
	//func("software"); //변경시킬수 없는 곳에 있는 메모리에 직접주는것임. literal에 직접 주는것은 막혀있다.
	func1("software");

	return 0;
}
  • String 1-2 (포인터)
#include <iostream>
#include <string>
using namespace std;

void func(char* str) { //문자열을 인자로 받는 함수
	str[0] = 'S';
}

void func1(const char* str) { //char에 포인터를 받을건데, 그 값을 변경시키지않을거야 (포인터가 가르키고있는 값을 변경하지않을것이다)
	//str[0] = 'S'; 바꾸려는 시도는 에러가남.
	char c = str[0]; //값을 읽으려는 시도는 괜찮음.
	c = 'S'; //읽은다음에 바꾸는건 상관없음. 자체를 바꾸는것이 나이므로
}

void func2(int* p) {
	*p = 10; //p가 가르키는 곳으로가서 그 값을 주겠다.
}

int func5() {
	int a = 10;
	return a;
}

int *func6() { //함수자체를 잘못만든것 (함수는 main메모리 위에 stack메모리에 생기는데, 주소를 main에 넘겨주면 본인 메모리가 사라짐), a의 주소는 사라진 주소이므로 그 주소로 가서 아무것도 할수없음.
	int a = 10;
	return &a;
}

int* func7() { //이건 가능 (main에서 함수를 부르면 new로 만들면 heap메모리에 생김 (동적메모리), 포인터는 heap메모리에 있는걸 가르키므로 본인 함수 메모리(stack)이 사라져도 접근가능)
	int *a = new int; //포인터로 넘겨주려면 동적으로 만들어야한다.
	*a = 10; 
	return a;
}

class Circle {
public:
	Circle() {
		x = 0;
		y = 0;
		r = 1;
	}
	Circle(int _x, int _y, int _r) {
		x = _x;
		y = _y;
		r = _r;
	}
	int x, y, r;
	void print() {
		cout << "[CIRCLE] (" << x << ","<< y << ") r=" << r << endl;
	}
};

int main() {
	string a("sejong university"); //문자열의 literal로 입력받는 함수는 const char*로 만들어야 받을수있음.
	cout << a << endl;

	//func("software")와 func(text)의 차이
	char text[] = "software"; //(프로그램 실행 내내 변하지않음) literal이므로 컴파일링할때 이미 정해져있어서 변하지않음.
	text[0] = 'S'; //배열을 만들어서 주게되면 복사본이 만들어지는것임. software라는 문자열은 사라지지않고 복사본의 값을 변화시키는것임.
	func(text);
	//func("software"); //변경시킬수 없는 곳에 있는 메모리에 직접주는것임. literal에 직접 주는것은 막혀있다.
	func1("software");

	int i;
	func2(&i); //이렇게 불러도 전혀 문제없음

	//func2(&(20))//절대 안댐 (literal의 주소는 넘길수없음)

	i = func5(); //a를 만들고 return 10을 함
	int* p = &i; //이건 가능
	//int* p; //이 포인터는 아무것도 가리키지않으므로 
	*p = func5(); //이건 안댐. (값을 줄수없음)
	p = func6(); //이것도 안됨.

	Circle c;
	c.print();

	Circle c1(10, 20, 30);
	c1.print();
	

	return 0;
}
  • 값에 의한 호출로 객체 전달
#include <iostream>
#include <string>
using namespace std;

void func(char* str) { //문자열을 인자로 받는 함수
	str[0] = 'S';
}

void func1(const char* str) { //char에 포인터를 받을건데, 그 값을 변경시키지않을거야 (포인터가 가르키고있는 값을 변경하지않을것이다)
	//str[0] = 'S'; 바꾸려는 시도는 에러가남.
	char c = str[0]; //값을 읽으려는 시도는 괜찮음.
	c = 'S'; //읽은다음에 바꾸는건 상관없음. 자체를 바꾸는것이 나이므로
}

void func2(int* p) {
	*p = 10; //p가 가르키는 곳으로가서 그 값을 주겠다.
}

int func5() {
	int a = 10;
	return a;
}

int *func6() { //함수자체를 잘못만든것 (함수는 main메모리 위에 stack메모리에 생기는데, 주소를 main에 넘겨주면 본인 메모리가 사라짐), a의 주소는 사라진 주소이므로 그 주소로 가서 아무것도 할수없음.
	int a = 10;
	return &a;
}



class Circle {
public:
	Circle() {
		cout << "기본 생성자 호출" << endl;
		x = 0;
		y = 0;
		r = 1;
	}
	Circle(int _x, int _y, int _r) {
		cout << "인자 있는 생성자 호출" << endl;
		x = _x;
		y = _y;
		r = _r;
	}
	int x, y, r;
	void print() {
		cout << "[CIRCLE] (" << x << ","<< y << ") r=" << r << endl;
	}
};

void func7(Circle d)
{
	d.print();
}

int main() {
	string a("sejong university"); //문자열의 literal로 입력받는 함수는 const char*로 만들어야 받을수있음.
	cout << a << endl;

	//func("software")와 func(text)의 차이
	char text[] = "software"; //(프로그램 실행 내내 변하지않음) literal이므로 컴파일링할때 이미 정해져있어서 변하지않음.
	text[0] = 'S'; //배열을 만들어서 주게되면 복사본이 만들어지는것임. software라는 문자열은 사라지지않고 복사본의 값을 변화시키는것임.
	func(text);
	//func("software"); //변경시킬수 없는 곳에 있는 메모리에 직접주는것임. literal에 직접 주는것은 막혀있다.
	func1("software");

	int i;
	func2(&i); //이렇게 불러도 전혀 문제없음

	//func2(&(20))//절대 안댐 (literal의 주소는 넘길수없음)

	i = func5(); //a를 만들고 return 10을 함
	int* p = &i; //이건 가능
	//int* p; //이 포인터는 아무것도 가리키지않으므로 
	*p = func5(); //이건 안댐. (값을 줄수없음)
	p = func6(); //이것도 안됨.

	Circle c(10, 20, 30); //컴퓨터는 내용값이 궁금하지않음
	c.print();
	func7(c); //in func7 stack memory, Circle d=c; 이 메모리는 초기값이 뭔지안다. (c에서 그대로 가져올것이므로) - 따라서 생성자가 굳이 불리지않음 (기본생성자가 호출되지않음)
	//1
	Circle e = c; //copy (그냥 12바이트를 그대로 복사함) - 기본생성자가 호출되지않음
	//2
	Circle e; //이럴때는 기본생성자가 한번 더 불림
	e = c;
	//1과 2는 다르다. why? 1은 copy (기본생성자가 호출되지않음) 2는 기본생성자가 한번 더 불림

	c.x = 1000; //copy or clone이 일어난것 (복사본이 생긴것)
	c.print(); //c랑 e는 메모리 공유하지않음.
	e.print();

	cout << sizeof(Circle) << endl;

	return 0;
}
  • 참조
#include <iostream>
#include <string>
using namespace std;
class Circle {
public:
	Circle() {
		cout << "기본 생성자 호출" << endl;
		x = 0;
		y = 0;
		r = 1;
	}
	Circle(int _x, int _y, int _r) {
		cout << "인자 있는 생성자 호출" << endl;
		x = _x;
		y = _y;
		r = _r;
	}
	int x, y, r;
	void print() {
		cout << "[CIRCLE] (" << x << "," << y << ") r=" << r << endl;
	}
};

void func7(Circle d)
{
	d.print();
}

void func8(Circle f) {
	f.r = 300;
	return;
}

void func9(Circle& f) { //아까있던 c와같은놈 (한번 별명을 만들면 죽을때까지 이놈임)
	f.r = 300; //이 메모리는 언제나 존재 (지울일도 건들일일도 없기때문에)
	f.print();
	return;
}

//func9와 같은 코드
void func10(Circle* p) { //* : 변수선언하면서 만들면 포인터라는 의미 //아까있던 c의 주소
	//1
	//(*p).r = 300; //포인터앞에 *이 있으면 그 포인터가 가리키는 값이 된다.
	//2
	p->r = 300;
	p->print();
	//1과 2는 같은것임

	////이거같은 경우
	//Circle d;
	//p = &d;
	//p->r = 300; 
	////이렇게 해버리면 중간에 p의 의미가 바껴버림 (주는건 c의 주소였는데, d의 주소로 존재하는 d를 바꿈)

	//포인터는 이 메모리가 유효한지 항상 확인해줘야함
	//ex) delete p;
	//	  p->r=300;
	//이미 없는 주소이므로 접근불가능 (하지만 레퍼런스는 다름)
	return;
}

void mySwap(int& a, int& b) //별명끼리 값을 바꾸는것
{
	int temp = a;
	a = b;
	b = temp;
}

//레퍼런스 변수를 통해 값을 리턴하는것도 가능
int  func10() {
	int a = 10;
	return a;
}

int & func11() {//이건 불가능 (return받는애한테 a의 별명을 주는것임)
	int a = 10;
	return a;
}

int gAAA = 30;
int& func12() { //이건 또 가능,,, (함수 리턴을 통해 다른놈 전달 가능)
	return gAAA;
}

int gArr[10];
int& func13(int ind) {
	////조건을 맞춰서 문제없이 만들수있음.
	//if(ind < 0) ind = 0;
	//if(ind > 10) ind = 9;
	//종료조건도 심어놓을수있음 (이것저것의 예시)
	if (ind < 0 || ind >= 10) { 
		cout << "Error!!!! - Out of Bound of gArr" << endl;
		exit(-1);
	}
	return gArr[ind];
}

int main() {
	Circle c(10, 20, 30); //컴퓨터는 내용값이 궁금하지않음
	c.print();
	func7(c); //in func7 stack memory, Circle d=c; 이 메모리는 초기값이 뭔지안다. (c에서 그대로 가져올것이므로) - 따라서 생성자가 굳이 불리지않음 (기본생성자가 호출되지않음)
	////1
	//Circle e = c; //copy (그냥 12바이트를 그대로 복사함) - 기본생성자가 호출되지않음
	////2
	//Circle e; //이럴때는 기본생성자가 한번 더 불림
	//e = c;
	//1과 2는 다르다. why? 1은 copy (기본생성자가 호출되지않음) 2는 기본생성자가 한번 더 불림

	c.x = 1000; //copy or clone이 일어난것 (복사본이 생긴것)
	c.print(); //c랑 e는 메모리 공유하지않음.
	//e.print();

	cout << sizeof(Circle) << endl;

	//참조는 이런식으로 사용하지않음 
	int aa = 10;
	int& r = aa; //참조는 단독으로 만들수없음 (기존(aa)의 별명이므로)
	r = 20; //aa의 값도 바뀜 
	cout << "a=" << aa << ", r=" << r << endl;

	int bb = 30;
	r = bb; //r은 언제나 a와 같은놈, 값에 복사가 일어나므로 bb의 값이 r로 가는거지, bb의 원래값은 변하지않음.
	r = 40;
	cout << "bb=" << bb << ", r=" << r << endl; //bb는 바뀌지 않음, r만 바뀜

	Circle& rr = c; //c와 똑같은 rr이 만들어짐 (c를 rr로 지칭)
	rr.print();

	//참조 사용은 이런식으로
	func8(c);
	c.print();

	func9(c);
	c.print();

	func10(&c); //& : 변수 선언시 쓰면 레퍼런스 변수를 만든다는 의미, 존재하는 변수앞에 있으면 그놈의 주소라는 의미
	c.print();

	//레퍼런수 변수의 존재의미는 포인터를 대체하는용 
	//장점 : 주소가 감춰짐 


	int aaa = 30;
	int bbb = 100;
	mySwap(aaa, bbb);
	cout << "aaa= " << aaa << ", bbb=" << bbb << endl;

	int ccc = func10();
	cout << "ccc=" << ccc << endl;

	int & ccc1 = func11(); //func11을 통해 a의 별명을 main에 주는데, ccc는 a와 같은데, func11은 더이상 존재하지 않고 사라지므로 본체가 사라지고 별명만 남아서 이상한값이 들어가게되는것 
	cout << "ccc1=" << ccc1 << endl;

	int& ccc2 = func12(); //(ccc2와 gAAA는 동체이다)
	ccc2 = 50;
	cout << "gAAA=" << gAAA << endl; //gAAA의 값

	func12() = 100; //이게 가능해짐, 리턴값이 gAAA자체이기때문에
	cout << "gAAA=" << gAAA << endl; //gAAA의 값

	//배열을 접근하는 또 다른 방식 : 장점 - 함수이므로 이것저것 할수있음
	for (int i = 0; i < 10; i++)  //func13()이 i번째 배열 그 자체와 같음
		func13(i) = i * 10; //함수는 i를 받아서 gArr[i]번째 값(레퍼런스)을 리턴함
	
	for(int i=0;i<10;i++)
		cout << "gArr[" << i << "]=" << gArr[i] << endl; //배열값 자체이므로 아무것도할수없음
	
	gArr[11] = 20; //아무문제없음
	func13(11) = 20; //에러메시지가 나옴 (문제가 생긴걸 알수있음) - 값의 유효성을 알수있어서 안전함

	//함수의 인자로 값을 변화시킬때 사용, 리턴값으로 그놈자체를 지칭할때 씀 (배열의 인덱스 접근을 수단으로써 사용가능)
	
	return 0;
}