#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 = ⅈ //포인터는 가르키고있는 그 주소에 가는것이 중요함
//포인터 변수 자체의 *의 의미 : 그 주소에 있는 값을 의미
(*ip) = 10; //p라는 포인터변수에 10을 넣는다.
//포인터에 변수 타입이 따라 붙는 이유 : 주소에 더해서 그 주소에 가면 int가 있다는것까지 표현해야 해서
//class 포인터
Line* p = &a;
(*p).print(); //괄호를 안치면 다른 의미가 된다.
a.pX = ⅈ
*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;
}
#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;
}
#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;
}
#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;
}
#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;
}
#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;
}