C++ 정리1

728x90

C++ 정리1

 

학교 시험 공부하면서 보완할 점을 정리했던 것들인데, 나중에 시간을 들여서 보기좋게 다듬을 예정이다.

 

C++ 창시자 : 비야네 스트롭스트룹
C언어의 문법을 계승하여 호환이됨.
객체 지향 개념 도입 : 캡슐화, 상속, 다형성

C++에서 추가된 내용이자 객체 지향의 특징들
함수중복, 디폴트 매개변수, 참조와 참조 변수, 참조에의한 호출, new/delete연산자
연산자 재정의, 제네릭함수 클래스 등....
*제네릭 함수 : 동일한 프로그램 코드에 다양한 데이터 타입을 적용할 수 있게 일반화 시킴.
(! 인라인 함수는 객체 지향 특징이 아님)

C++은 객체지향언어
class 안, 밖 모두에 변수, 함수 선언 가능
C++은 함수 중복(오버로딩)과 다중상속(C++만 가능) 가능

캡슐화 : 데이터를 외부의 접근으로부터 보호함. (클래스로 표현)
상속 : 부모 클래스의 멤버를 자식 클래스가 가져옴.
다형성 : 하나의 기능이 경우에 따라 다르게 보이거나 다르게 작동하는 현상
연산자 중복, 함수 중복, 함수 재정의(오버라이딩)

라이브러리 가져올때는 <> 직접 만든거 가져올때는 " .h"
main()만 실행되며 프로젝트하나에 main은 하나만 존재.
두 개 이상인경우 컴파일은 되더라도 링크에러가 난다.
실행파일 하나에 main 하나는 필수이다.

namespace 명시안하면
std::cout 이런식으로 사용
return 0; 는 main에서만 생략가능한게 표준

장점 : C언어와의 호환성
단점 : 캡슐화의 원칙이 무너저 전역변수 전역함수를 반드시 사용해야함.

C++은 0,1 true, false 모두 가능
true를 cout으로 출력하면 1이 출력됨.

cpp파일들은 서로 독립적으로 컴파일되어 각각의 obj파일을 만듬
헤더파일을 만드는 이유 :
오브젝트 파일에 있는 함수들의 내용을 다른 소스파일에서도 사용할 수 있도록 하기 위함


헤더 파일은 컴파일 되지 않는다. (obj파일 안만듬)
헤더 파일은 include 한 cpp 파일 내에 전체 복사될 뿐이다.
cpp 파일들은 각각 독립적으로 컴파일 된다.
그 다음에 컴파일이 완료된 cpp 파일들을 링킹 한다.

표준 C++ 라이브러리 헤더파일은 확장자 자체가 !원래 없으므로 !
그냥 #include <iostream> 이런식으로 확장자 없이 쓴다.

함수 원형이 필요한 이유
매개변수 개수가 올바른지, 매개변수의 자료형이 정확한지, 
리턴값에 대한 처리가 메인에서 지정한 것과 동일한지 확인하기 위한 용도

함수의 선언부를 미리알려줌으로써 위에서부터 먼저 원형을 파악한 컴파일러가
구현부가 어딘가 있음을 인지하는 방법으로 컴파일을 용이하게 할 수 있다.


main이 포함된 파일의 함수는 다른 곳에서 참조하기가 어렵다.
따라서 잘 작성된 함수부분만 따로 분리하도록 한다.

소스를 분리하지 않으면 많은 분량을 컴파일 하는 일을 반복하게 된다.
(컴파일의 편리)

소스를 분리하면 다른 사람과 공유하기 쉽다.
(공유의 편리)




namespace -> 이름충돌로 인한 문제점 해결
공동 프로젝트나 다른 소스를 가져와 컴파일/링크할 때 충돌이 발생할 수 있음.

개발자가 자신만의 이름 공간을 생성해 다른 이름공간과 구분되도록 함
iu.h
namespace iu {
int f();
void m();
}

iu.cpp
#include "gong.h"
namespace iu{
int f() { return 1; }  
void m() {
f(); // iu 이름공간 내에 있는 f를 사용
gong::f(); //다른 소스파일의 같은 함수를 사용
}
}

gong.h
namespace gong{
int f();
int g();
}

gong.cpp
#include <iostream>
namespace gong {
int f() { return -1; }
int g() { return 0; }
}

cout의 디폴트 실수 표현은 6자리.

cin의 특징 : 입력버퍼를 내장하고 있어서 엔터 입력전까지 키 값을 입력 버퍼에 저장하고 있다가
엔터 입력하면 입력버퍼에서 키 값을 변수로 전달

C스트링 방식 (문자열 배열은 \0 널문자로 끝나야 한다.)
char name[4] = {'G', 'O', 'M', '\0'}; // 문자열로 취급
char name[4] = {'G', 'O', 'M', 'E'};  // 단순 문자 배열 취급
char name[10] = "Grace"; 인덱스 0~4까지 각 문자 할당, 5번에 널문자할당, 나머지 널문자로 초기화

char name[6]; // 5개의 문자를 저장할 수 있는 배열이다.
cin >> name; //G r a c e \0 이렇게 총 6개가 들어간다.

C스트링 방식은 true false가 안되므로 문자열간의 비교를 위해서
#include <cstring>을 불러오고
if(strcmp(password, "C++") == 0 ) 과 같은 방법을 사용한다. (두 값이 같을 때 0을 반환!!!!!)
strlen() -> 문자열의 길이를 출력할 때 사용. ()안에 배열도 가능.
char a[100] = "안녕하세요?";
cout << strlen(a);  // 17

공백이 낀 문자열을 받으려면 cin.getline(변수명, 길이, 마지막 기호);
마지막 기호자리에 '\n'을 입력하면 엔터를 입력하기 전까지의 값을 변수에 할당
cout << "주소 입력 : ";
char address[100];
cin.getline(address, 100, '\n');
cout << "주소는 " << address << "입니다" << endl;

cin.getline(buf, 100, ';');
buf는 char buf[100];으로 선언하면 적당함.
그냥 string buf; 하면 당연히 에러남.
최대 99개의 문자를 읽어 buf에 저장함. 맨 마지막에 '\0'을 붙임

string 클래스 방식
C++에서 강력추천. string클래스 지알아서 문자열 크기 맞게 내부 버퍼 조절해서 편함
#include <string>필요
getline(cin, 변수명); //getline은 string타입 문자열 입력 받기 위한 전역 함수.

#include <iostream>
#include <string>
using namespace std;

int main(){
    string test;
    getline(cin, test);   // 무조건 변수가 string으로 선언되었어야함.
    cout << test << endl;
    return 0;
}
string으로 변수선언했다면 cin, getline 모두 사용할 수 있으나
공백을 포함할 수 있는게 getline방식 뿐이란 것임.

string 클래스 사용시 if(문자열1 == 문자열2) 비교 가능
C++은 문자+문자열 , 문자열+문자열 연산이 가능함.
string 형식의 배열변수[0] == 배열변수.at(0);


cout은 함수가 아니라 표준 출력 객체 이름이다. ()가 없음.
<< , >>연산자는 원래 비트shift연산자였는데 스트림 삽입 연산자로 재정의 됨.
이를 다형성이라고함.(연산자 재정의, 연산자 중복, 연산자 오버로딩) - OOP의 특징

---------------------------------------------------

클래스명은 숫자, _도 가능함.
클래스 내부 변수 : 멤버변수
클래스 내부 함수 : 멤버함수
클래스로 감싸는 것을 캡슐화라고 함.
함수 내부변수 : 지역변수
중괄호에 속하지 않은 변수 : 전역변수
모든 클래스는 데이터 타입이 될 수 있음.
생성자 이름은 클래스 이름하고 똑같음. 그러나 멤버함수와 달리 리턴타입이 없음.
생성자의 주 역할은 멤버변수의 초기화임.
멤버함수는 반드시 무언가를 반환해야함. 반환할게 없어도 void로 선언되기라도 해야함.

접근지정자의 디폴트값은 private
public : 클래스 내부외부 접근 가능
private : 클래스 내부에서만 접근 가능
protected : 상속관련 내용인데 있다는 것만 알아두삼.
한 클래스 안에서 여러개의 접근 지정자 가능함.

클래스 중괄호 종료시 반드시 세미콜론 붙이기.

생성자는 객체 생성시 자동으로 단 한번만 액션
소멸자는 메모리에서 사라질 때 딱 한번 액션

생성자 코딩 안하면 목적파일에 컴파일러가 자동으로 아무것도 없는 형태로 만들어줌.
생성자는 여러개 선언 가능.(생성자 오버로딩 : 같은 이름이지만 매개변수 받는 부분과 기능이 달라짐)

#include <iostream>
using namespace std;
class Circle{
public:  // 여기를 지우면 디폴트가 private이므로 클래스 외부에서 생성자에 접근이 불가능해짐.
    int rad;
    Circle() { rad = 1; } // 생성자는 반환할게 없음. 리턴생략이 가능한거임. return; <- 써도 틀린거 아님.
    double getArea() {  // 멤버함수는 리턴타입이 필요하고 없어도 void로 잡아줘야함.
        return 3.141592 * rad * rad;
    }
};

return의 의미 : 지금함수의 종료, return 오른쪽 값 반환


int main() {
    Circle donut;
    cout << "donut 면적은 " << donut.getArea() << endl;

    Circle pizza;
    pizza.rad = 10;
    cout << "pizza 면적은 " << pizza.getArea() <<endl;
}

객체 생성은 연속적으로도 가능함. 단, 생성자의 매개변수가 객체에 부여하는 인수의 개수와
같은 것이 있는지는 잘 확인하도록 해야함.
Circle twin, stra; 위의 코드에서는 이런식으로 객체 생성이 가능.
만약 Circle(int r) {rad = r;}과 같은 생성자가 있었다면
Circle house(2), hotel(34); 이런식으로 객체 생성도 가능함.


위 코드의 생성자 소멸 순서는 pizza, donut순(스택처리)
객체 소멸 시기는 속해있는 함수의 중괄호가 닫히거나 return 반환시에 처리됨.
소멸자는 객체가 소멸할때 자동으로 호출됨.
소멸자 또한 코딩 안하면 컴파일러가 기본 소멸자를 만듬.(생성자도 마찬가지임)
다만 소멸자는 딱 하나만 존재. 오버로딩 안되고 리턴타입, 매개변수도 없음.

지역 객체 : 함수 내 객체, 함수 종료시 소멸
전역 객체 : 함수 바깥 객체, 프로그램 종료시 소멸

객체 생성 순서
전역 객체 : 프로그램에 선언된 순서
지역 객체 : 함수가 호출되면 그 안에서 순서대로

객체 소멸 순서
함수가 종료하면 지역 객체가 생성 순서의 역순으로 소멸
프로그램 종료시 전역 객체가 생성 순서의 역순으로 소멸

전역객체가 제일 먼저 메모리에 자리잡고 객체가 생성된다는 것을 기억하자.
그다음 메인에 있는 객체,  그 다음 메인안에서 호출한 함수내에 있는 객체 순으로 생성
소멸은 그 함수가 끝날때 함수에서 선언된 순서의 역순으로 소멸,
메인안에서 선언된 생성 순서의 역순으로 소멸,
최종적으로 전역객체의 생성순서의 역순으로 소멸로 끝


#include <iostream>
using namespace std;
class Circle{
    int rad;  // private상태가 됨.
public:
    Circle() { rad = 1; } // 생성자는 리턴타입이 없음.
    double getArea() {  // 멤버함수는 리턴타입이 필요하고 없어도 void로 잡아줘야함.
        return 3.141592 * rad * rad;
    }
};

int main() {
    Circle donut;
    cout << "donut 면적은 " << donut.getArea() << endl;  // 내부 접근 가능.

    Circle pizza;
    pizza.rad = 10;  // 객체 내 rad 멤버변수가 private이므로 접근 불가능. 에러 발생
    cout << "pizza 면적은 " << pizza.getArea() <<endl; 
}

만약 클래스 내의 멤버변수 선언위치가 좀더 아래에 있다고 해도 컴파일러가 알아 먹음.
(public 상태라면)


struct Circle{
public:  
    int rad;
    Circle() { rad = 1; } 
    double getArea() { 
        return 3.141592 * rad * rad;
    }
};

c++에서는 struct 안에도 함수가 들어갈 수가 있음. class의 확대개념으로 생각하기 때문.
단 struct의 디폴트 지정자는 public으로 인지됨.

class 키워드는 소문자라는 것을 기억하기.
선언부의 함수 원형에는 {}가 필요없음. 대신 ; 을 붙여야만 함.

헤더파일 참조의 중복을 방지하는 키워드
#ifndef CIRCLE_H  // CIRCLE_H 가 정의 되지 않아 실행
#define CIRCLE_H // CIRCLE_H 정의
class Circle {
...
};
#endif // 끝

구조체 : 다양한 데이터 타입 넣기 가능
배열 : 같은 데이터 타입 넣기만 가능

클래스가 객체 생성으로 인해 메모리에 자리를 잡으면 객체라고 부름.


3단분리 방법
헤더파일에는 클래스 선언부를,
cpp파일에는 클래스를 구현하고 선언되었던 헤더파일을 include해온다.
main()이나 다른 cpp에서 위의 클래스를 필요로할 때 클래스 선언한 헤더파일을 include한다.
이를 통해 클래스를 쉽게 재사용하고 분리 저장 작성할 수 있다.

Circle.h
#ifndef CIRCLE_H
#define CIRCLE_H
class Circle{
public: 
    int rad;
    Circle();
    double getArea();
};
#endif

Circle.cpp
#include "Circle.h"
Circle::Circle() { rad = 1; }
double Circle::getArea() { return 3.14*rad*rad; }

main.cpp
#include "Circle.h"
#include <iostream>
using namespace std;
int main() {
    Circle donut;
    cout << "donut 면적은 " << donut.getArea() << endl;

    Circle pizza;
    pizza.rad = 10;
    cout << "pizza 면적은 " << pizza.getArea() <<endl;
}
circle.cpp와 main.cpp에서 목적파일이 하나씩 생기고,
이 두개의 목적파일을 링크작업을 통해 하나의 실행파일을 생성하게 한다.
main.cpp에서 include한 circle.h 헤더파일은 클래스의 선언부를 작성한 헤더파일을 가리킨다.




메인에서만 선언부 구현부 메인부 나누기
#include <iostream>
using namespace std;
class Circle{
public:
int rad;
Circle();
double getArea();
};
Circle::Circle() { rad = 1; }
double Circle::getArea() {
return 3.14 * rad * rad;
}
int main() {
Circle donut;
cout << "donut 면적은 " << donut.getArea() << endl;

     Circle pizza;
     pizza.rad = 10;
     cout << "pizza 면적은 " << pizza.getArea() <<endl;
}


멤버 변수 초기화 방법
class Point {
int x, y;
public:
Point();
Point(int a, int b);
};
1. 구현부에서 초기화
Point::Point() { x = 0; y = 0;}
Point::Point(int a, int b) { x=a; y=b; }  // 생성자는 아무것도 반환하지 않는다.

2. 
Point::Point() : x(0), y(0) {}
Point::Point(int a, int b)
: x(a), y(b) {}

3. 선언부에서 초기화
class Point {
int x=0, y=0;
public :
...
};

객체 캡슐화 전략
중요 멤버는 다른 클래스, 객체에서 접근 못하게 보호
일부 멤버만 접근 허용 getter, setter(public)함수 : 외부에서 내부 멤버변수 바꾸게 함.
getter 읽기 
setter 쓰기

예를 들어 멤버 변수는 private내에 선언.
메인에서 이 멤버변수에 대한 접근이 불가능하기 때문에
클래스 구현부에 있는 멤버함수로 멤버변수의 값을 변경하고 메인으로 반환하는 방법으로
사용할 수 있다.

#include <iostream>
using namespace std;
class Account {
    int balance; // private
public:
    Account() : balance(10000) {}
    int getBalance() { return balance; } //읽기 (멤버변수의 값을 읽음 : getter)
    void setBalance(int amount) { balance += amount; } // 쓰기 (멤버변수의 값을 변경 : setter)
};

int main() {
    Account a;
    int money;
    cout << "입금액 입력 : ";
    cin >> money;
    a.setBalance(money);
    cout << "출금액 입력 : ";
    cin >> money;
    a.setBalance(-money);

    cout << "잔액 : " << a.getBalance() <<endl;
}





생성자도 private 선언이 가능하다. 한 클래스에서 오직 하나의 객체만 생성할 수 있도록 하기 위함.
이럴경우 private 내에 존재하는 생성자는 외부에서 호출할 수 없게 된다.



인라인 함수와 자동 인라인 함수

다음과 같은 코드는 odd(i)가 10000번이나 호출되면서 엄청난 오버헤드 시간이 소모가 된다.

(함수는 호출될 때마다 발생하는 일정량의 성능 오버헤드가 있기 때문)

x%2를 계산하는 시간보다 odd() 함수 호출에 따른 오버 헤드가 더 크며, 루프를 돌게 되면 오버헤드가 가중되는 문제가 발생한다.

#include <iostream>
using namespace std;

int odd(int x) {
    return (x%2);
}
int main() {
    int sum = 0;
    for (int i = 0; i <= 10000; i++) {
        if(odd(i))
            sum += i;
    }
    cout << sum;
}
 

인라인 함수는 이렇게 자주 호출되지만 함수의 코드가 짧을 경우에 시간 소모를 줄일 수 있는 방안이 된다.

인라인 함수로 호출하면 함수 호출에 따른 오버헤드가 존재하지 않으며, 프로그램 실행 속도가 개선된다.

함수가 호출이 함수 자체 내용 복사본으로 대체되기 때문이다.

#include <iostream>
using namespace std;

inline int odd(int x) { // 인라인 함수로 선언
    return (x%2);
}
int main() {
    int sum = 0;
    for (int i = 0; i <= 10000; i++) {
        if(odd(i))
            sum += i;
    }
    cout << sum;
}
 

 

(코드로 입력한 메인 함수)

int main() {
    int sum = 0;
    for (int i = 0; i <= 10000; i++) {
        if(odd(i))  // 함수 호출 부분
            sum += i;
    }
    cout << sum;
}
 

(컴파일러가 자체적으로 함수 내용을 대체한 코드. 함수를 인라인 함수로 처리하기 때문에 오버헤드 
문제가 해결된다.)

int main() {
    int sum = 0;
    for (int i = 0; i <= 10000; i++) {
        if(i%2)  // 컴파일러가 확장시킨 코드
            sum += i;
    }
    cout << sum;
}
 

 

인라인 함수를 사용하면 프로그램의 실행 시간이 빨라진다는 장점이 있으나, 컴파일된 전체 코드의 
크기가 증가하기 때문에 함수의 내부 코드가 짧은 함수에 적합하다. 

 

만약 클래스의 선언부에 함수를 구현했다면 inline으로 함수를 선언하지 않아도 컴파일러에 의해 
자동으로 인라인 처리가 된다. 생성자를 포함한 모든 함수가 자동 인라인 함수로 기능할 수 있다.

인라인 함수 요청 두가지 방법.
그냥 inline 키워드를 함수 맨 앞에 적는다.
자동 인라인 : 클래스의 선언부에서 그냥 구현까지 해버리면 됨.
클래스 밖의 일반 함수를 인라인함수로 요청하려면 그냥 inline 키워드 쓰먼됨.

#include <iostream>
using namespace std;
class TV{
    int channels;
public:
    TV() { channels = 256; }  // 자동인라인
    int getChannels() { return channels; }  // 자동 인라인 (생성자를 포함한 모든 멤버함수가 인라인 함수가 될 수 있음)
    void setChannels(int channels); // 구현이 없으므로 함수 원형임.
};
inline void TV::setChannels(int channels){  // inline 키워드 썼기 때문에 자동인라인은 아님.
    this->channels = channels;
}
int main(){
    TV myTV;
    cout << myTV.getChannels() << endl;
}


구조체
클래스처럼 상속 멤버 접근지정 다 똑같음
대신 구조체 디폴트 접근지정자는 public
클래스는 private
C언어와 호환되어서 좋고,
구조체 객체를 생성할때
이전에 struct 구조체명 객체명; 방식에서
구조체명 객체명; 방식으로 생략하는 방법을 차용했음.
typedef 이런거 없어도 생략 가능함.
struct StructName{
private:
...
};
main...
structName stObj; 


C++ 구조체는 클래스와 거의 같아서 초기화도 할 수 있음.


배열 0 초기화 방법
int a[5] = {0,0,0,0,0};
int a[5] = {0, };
int a[5] = {0};
int a[5] = {7,}; // {7, 0, 0,....}

int a[5];
a = {7, 7, ...} 당연히 이런 방법은 불가능
반복문으로 하나씩 바꿔줘야함
for (int i = 0; i<5; i++) a[i] = 7;

fill_n(a, 5, 7); -> a라는 배열의 5개 원소를 전부 7로 치환함.

int c[3][5] = { {0,0,0,0,0} ... };
int c[3][5] = {0,};
int c[3][5] = {0};

int c[3][5];
for (int = 0; i<3; i++)
for (int j=0; j<5; j++)
c[i][j] = 7;

fill_n(c[0], 15, 7); // 이차원배열은 일차원배열의 첫번째 주소를 가리키도록 한다.)


------------------------------------------------------

포인터
int *p = &i;
또는
int *p;
p = &i;


동적 메모리 할당
필요한 양이 예측되지 않는경우. 프로그램 작성 시 할당 받을 수 없음.
실행중에 힙 메모리에 할당
힙은 운영체제가 프로세스의 실행을 시작시킬 때 동적 할당 공간으로 준 메모리 공간.

new연산자  : 힙 영역에 메모리 할당
delete 연산자 : 메모리 반환. 객체의 경우 소멸자 호출 뒤 객체를 힙에 반환.

타입 *포인터변수 = new 타입;
delete 포인터변수;
delete 후 포인터 p는 살아있지만, p가 가리키는 곳에 접근하면 안됨.
#include <iostream>
using namespace std;
int main(){
    int *p;
    p = new int;
    if (!p){  // p가 NULL이면 메모리 할당 실패
        cout << "메모리 할당 불가";
        return 0;
    }
    *p = 5;
    int n = *p;
    cout << "*p = " << *p << endl;
    cout << "n = " << n << endl;
    delete p;
}

배열 동적할당
int *p = new int [5];
for (int i = 0; i<5; i++)
p[i] = i;
delete [] p;


#include <iostream>
using namespace std;
int main(){
    int n;
    cout << "입력할 정수 개수 : ";
    cin >> n;
    if (n <= 0){
        cout << "양수 입력 : "; return 0;
    }
    int *p = new int[n];
    if(!p){
        cout << "메모리 할당 불가."; return 0;
    }
    cout << n << "개의 정수 입력.\n";
    for (int i = 0; i < n; i++){
        cin >> p[i];
    }
    cout << "거꾸로 출력.\n";
    for (int i = n-1; i >=0 ; i--){
        cout << p[i] << ' ';
    }
    delete [] p;
}

동적할당 메모리 초기화는 배열만 불가능.
int *pint = new int(20);
char *pchar = new char('a');
배열은 불 가 능
배열 메모리 반환은 반드시 delete [] p;

객체의 경우
클래스이름 *포인터변수 = new 클래스이름;
클래스이름 *포인터변수 = new 클래스이름(생성자 매개변수);
delete 포인터변수;

Circle *p *q; 이런식으로 여러개 선언가능
생성자 매개변수가 없는경우
p = new Circle;
있는경우
q = new Circle(30);

프로그램 종료시 운영체제는 누수 메모리를 모두 힙에 반환



this 포인터
객체 자신 포인터
클래스 멤버 함수 내에서만 사용(일반함수, static멤버 함수에서는 사용 불가능)
개발자가 선언하는 변수가 아닌 컴파일러가 선언한 변수
class Circle{
int rad;
public:
Circle() { this->rad=1; }
Circle(int rad) { this -> rad = rad; }
void setRad(int rad) { this -> rad = rad; }
};

클래스 내부에서 멤버 변수는 그냥 이름을 부르면 됨.
예를들어 int radius;라는 멤버 변수가 있을 때
선언부든 구현부든 상관없이 radius라고 부르면 멤버변수를 의미함.

하지만 이름이 충돌할 경우가 있음.
멤버함수에서 radius라는 지역변수(입력인자 매개변수 포함) 선언 가능함.
이 경우에 한해서 그냥 radius라고 부르면 지역변수가됨.
따라서 지역변수 radius말고 멤버변수 radius를 가리키려면 this->radius라고 하면 됨.
(java의 this.radius와 같음)




class Student{
int number;
string name;
double grade;
// 멤버변수 선언에는 이상없음
};
int main(){
Student s = {123, "고흐", 3.7};  // 메인에서 멤버 변수 초기화 시도에서 에러 발생
클래스 기본 생성자(컴파일러가 만들어주는) 접근지정자는 private이므로
변수 선언이 가능하나 메인에서 그 변수를 초기화 하려는 시도는 클래스 내의
멤버 변수가 private이기 때문에 에러가 난다. 따라서 이를 가능하게 하려면
멤버변수를 public으로 선언해야 한다.

Student s = {123, "고흐", 3.7};
Student *p
p = &s;
포인터로 객체의 멤버변수에 접근하는 3가지 방법
s.name
(*p).name
p -> name


포인터를 이용한 swap 함수 구현
#include <iostream>
using namespace std;
void myswap(int *a, int *b){
    int tmp = *a;
    *a = *b;
    *b = tmp;
}
int main(){
    int a, b;
    cout << "두 수 입력 : ";
    cin >> a >> b;
    cout << "swap전 : " << a << ' ' << b <<endl;
    myswap(&a, &b);
    cout << "swap후 : " << a << ' ' << b <<endl;
}

참조변수를 이용한 방법
#include <iostream>
using namespace std;
void myswap(int &a, int &b){
    int tmp = a;
    a = b;
    b = tmp;
}
int main(){
    int a, b;
    cout << "두 수 입력 : ";
    cin >> a >> b;
    cout << "swap전 : " << a << ' ' << b <<endl;
    myswap(a, b);
    cout << "swap후 : " << a << ' ' << b <<endl;
}

int a[] = {10,5,7,100,99};
int *p = a; // 배열명 자체가 시작 주소이므로 &필요없음.
cout << a << p << endl;  //둘다 배열의 시작주소
cout << a[2] << p[2] << endl; //둘다 7
p++; //메모리 시작주소 한칸 이동.
cout << p[2] <<endl; // 100
a++; // 에러발생이유, 배열명은 상수선언되어있으므로 움직일 수가 없음.


a가 배열일 때 cout << a << endl;
배열명을 넣고 출력하면 배열의 시작주소가 출력됨.
단 char형 배열은 그대로 출력하려고 하면 char배열은 문자열로 간주하므로 널을 만날때 까지 
계속해서 모든 문자가 출력됨. 
따라서 char 변수 안에 값 중간에 널문자가 끼어있으면 딱 널문자까지만 출력하고 나머지는 무시된다.


포인터를 활용한 이차원 배열 출력방법
#include <iostream>
using namespace std;
int main(){
    int c[3][5] = {{1,3,5,77,9}, {10,20,3,4,-1},{0,0,8,5,3}};
    int *p = c[0]; // 또는 &c[0][0]; 이차원배열내의 일차원 배열의 시작값을 가리키도록 만든다.
    for (int i = 0; i<15; i++){
        cout << p[i] << ' ';
        if (i % 5 == 4)
            cout << endl;
    }
}


이차원배열간 덧셈뺄셈과 클래스 활용
#include <iostream>
using namespace std;

class ThreeMatrices{
    int a[3][5] = {{5,10,2,7,5},{4,6,2,2,9},{1,9,2,8,4}};
    int b[3][5] = {{5,2,7,4,5},{10,6,9,2,3},{2,6,4,7,1}};
    int c[3][5] = {0};
public:
    void buildC(char op);
    void printC();
};
void ThreeMatrices::buildC(char op){
    for (int i = 0; i < 3; i++){
        for (int j = 0; j< 5; j++){
            if (op == '+'){
                c[i][j] = a[i][j] + b[i][j];
            }
            else{
                c[i][j] = a[i][j] - b[i][j];
            }
        }
    }
}
void ThreeMatrices::printC(){
    for (int i = 0; i < 3; i++){
        for (int j = 0; j < 5; j++){
            cout << c[i][j] << ' ';
        }
        cout << endl;
    }
}
int main(){
    ThreeMatrices m;
    cout << "Add..." <<endl;
    m.buildC('+');
    m.printC();

    cout << "Sub..." <<endl;
    m.buildC('-');
    m.printC();
}

생성자와 객체배열 선언
생성자가 Circle(int r) { ... }일때
Circle Arr[3] = {Circle(1), Circle(2), Circle(3)};
Circle Arr[] = {Circle(1), Circle(2), Circle(3)};
Circle Arr[] = {1, 5, 10, 15, 20};

생성자가 Circle(int r, int n) { ... }일때
Circle Arr[3] = {Circle(1,3), Circle(2,3), Circle(3,2)};
Circle Arr[] = {Circle(1,3), Circle(2,3), Circle(3,2)};
Circle Arr[] = {{1,3}, {5,5}, {10, 99}};  // {(1,3), (5, 5)...} 이건 안됨.

객체배열 활용
#include <iostream>
using namespace std;
class Circle{
    int rad;
public:
    Circle(int r) {rad = r;}
    double getArea() { return 3.141592*rad*rad; }
    double getCircustance() { return 2*3.141592*rad; }
};
int main() {
    Circle cArr[] = {1,5,10,15,20};
    for (int i =0; i<5; i++){
        cout << "Circle " << i << "의 면적은 " << cArr[i].getArea();
        cout << " 둘레는 " << cArr[i].getCircustance() << endl;
    }
}

코드 영역 : 실행된 코드가 기계어 형태로 저장됨
데이터 영역 : 전역변수 저장
힙 영역 : 동적할당된 변수가 할당
스택영역 : 지역변수 저장


 

728x90