포인터 | 래퍼런스 | 클래스

김여울·2025년 6월 2일

내일배움캠프

목록 보기
12/139

오늘은 배열의 평균값을 풀었다. 꽤 어렵다...

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

double solution(int numbers[], size_t numbers_len) 
{
	int sum = 0;

	for (int i = 0; i < numbers_len; i++)
	{
		sum += numbers[i];
	}
	
	return (double)sum / numbers_len;
}
  • int numbers[]
    정수 배열 (리스트처럼 쓰는 거)

  • size_t numbers_len
    배열의 길이 (몇 개 있는지)

  • (double)sum / numbers_len
    소수점 계산하려고 형변환

  • size_t
    양수만 저장할 수 있는 정수형 타입
    배열 길이, 메모리 크기, 반복문 인덱스 등에 안전하게 쓰이는 자료형

    intsize_t
    음수 포함됨항상 0 이상 값만 가능
    32비트 제한 있음시스템에 따라 더 크거나 안전함
  • for (int i = 0; i < numbers_len; i++)
    i = 0부터 시작해서
    i가 numbers_len보다 작을 동안
    i를 하나씩 증가시키면서 반복
    → 배열의 0번째부터 끝까지 하나씩 검사함

 numbers = [10, 20, 30]
 numbers_len = 3
  • sum += numbers[i];
    배열 값들을 하나씩 sum에 더함 (총합 구함)



C++ 기본 문법을 다 듣고 포인터와 래퍼런스, 클래스 강의를 들었다. 사전 캠프 강의에서 들었던 내용과 겹쳐서 수월할 줄 알았는데 단어만 익숙할 뿐 어려웠다.

📎 강의 자료 + 필기

포인터와 래퍼런스

포인터 아직도 헷갈림.. 유튜브에 검색해서 영상 찾아봄...ㅎ
📎개발자로의 기로에 서게 했던 ... C언어 포인터 문제 (from 정보처리기사 정처기 문제)

1️⃣ 포인터 개념

int x = 10;
int* p = &x;
표현의미값의 예
x변수 자체10
&xx의 주소값0x100
p주소를 저장한 포인터0x100
*pp가 가리키는 값 (x)10
x:  [ 10 ]     ← 실제 데이터
     ↑
   0x100       ← 주소값

p:  [ 0x100 ]  ← 주소 저장

*p == 10       ← p가 가리키는 곳의 값

2️⃣ 배열과 포인터

int a[3] = {10, 20, 30};
int* p = a;

// a == &a[0]
// p = a;는 곧 p = &a[0];
표현의미예시
pa[0] 주소0x200
*pa[0] 값10
*(p + 1)a[1] 값20
*(p + 2)a[2] 값30
a[0]:  [ 10 ] ← 주소: 0x200
a[1]:  [ 20 ] ← 주소: 0x204
a[2]:  [ 30 ] ← 주소: 0x208

p = a;

*p       → 10
*(p + 1)20
*(p + 2)30

3️⃣ 포인터 vs 배열

  • 배열 ( int a[3] )
    • 같은 타입의 변수들을 모아놓은 연속된 메모리 공간
    • 컴파일 타임에 크기 결정
    • 이름(a)은 상수 포인터처럼 동작
      (주소만 나타냄, 재할당 불가)
  • 포인터 ( int* p )
    • 주소값 저장
    • 동적으로 다른 주소 할당 가능
    • new 키워드로 동적 메모리 할당 가능
int* p = new int[3]; // 동적 배열
p[0] = 10;
p[1] = 20;
p[2] = 30;

4️⃣ 역참조

*는 무조건 "주소를 역참조"할 때만 써야 한다
주소 없이 * 쓰면 세그먼트 오류(잘못된 메모리 접근)

int x = 10;
int* p = &x; // p는 x의 주소를 저장
*p = 20;     // x의 값이 20으로 바뀜

p는 유효한 주소를 가지고 있음 (&x)
그래서 *p는 x의 값에 접근 가능

int* p;   // 선언만 하고 주소를 안 넣음
*p = 10;  // ❌ 큰일남!!!

p는 지금 아무 주소도 안 가리키고 있어
그런데 *p를 쓰면 "어디인지도 모르는 메모리 공간을 열어보는 것" → ❌

5️⃣ 래퍼런스

변수에 다른 이름(별명)을 붙여주는 것
선언할 때 & 기호를 사용하지만 주소가 아니라 레퍼런스 타입을 의미

int x = 10;
int& ref = x; // ref는 x의 또 다른 이름
ref = 20;     // x도 20이 됨

ref는 x의 레퍼런스
ref에 값을 바꾸면 x도 같이 바뀜
실제로는 둘이 같은 메모리 공간을 공유함

x:   [ 10 ] ← ref도 이 공간을 그대로 가리킴

ref = 20;

결과:
x:   [ 20 ]

6️⃣ 포인터 vs 래퍼런스

항목포인터 (*)레퍼런스 (&)
선언 방식int* p = &x;int& ref = x;
주소 저장 가능가능 (p = &x)불가능 (다른 대상의 별명이 될 수 없음)
초기화 없이 선언가능 (int* p;)불가능 (int& ref; ❌ 오류)
재할당 가능 여부가능 (다른 주소도 넣을 수 있음)불가능 (처음 연결된 변수만 참조)
값 접근 방식*p그냥 ref
NULL 설정 가능가능 (p = nullptr;)불가능 (null 레퍼런스 없음)

Class

클래스를 사용하는 이유 : 재사용성

1️⃣ 멤버 함수와 멤버 변수

✅ 멤버 함수 (함수 = 동작)

  • 역할

    • 객체가 할 수 있는 동작(기능) 정의
  • 특징

    • 외부에서 접근 가능 (public)
    • 객체를 사용할 때 호출됨

✅ 멤버 변수 (변수 = 데이터)

  • 역할
    • 객체가 가지고 있는 세부 정보(상태) 저장
    • 외부에서는 접근 불가 (private)
    • 멤버 함수에서만 사용
    • 내부 연산이나 상태 보존에 사용됨

→ 필요한 동작만 공개하고 세부 데이터는 숨김

class AMyActor : public AActor
{
public:
    // 외부에서 접근 가능한 기능
    void Jump();
    void TakeDamage(int Amount);

private:
    // 외부에 노출하지 않는 세부 정보
    int CurrentHP;
    float MoveSpeed;
};  

2️⃣ 멤버 함수 구현

구현 위치에 따라 2개의 방식이 있다

✅ 클래스 내부에서 직접 구현하는 방법
➡ 클래스 안에서 바로 함수 본문까지 작성

✅ 클래스 내부에서는 선언만 하고 외부에서 구현하는 방법
➡ 클래스 안에서는 함수 이름만 적고,
➡ 밖에서 클래스이름::함수이름으로 정의

  • 파일로 나누기

    • 헤더 파일(.h): 클래스 선언 + 함수 선언
    • 소스 파일(.cpp): 함수 정의
  • 클래스 외부에서 멤버 함수 정의할 때 구조

반환형 클래스이름::함수이름(매개변수)
{
    // 함수 내용
}

// 예시
double Student::getAvg()	// Student 클래스의 멤버 함수이다
{
    return (kor + eng + math) / 3.0;
}

3️⃣ 클래스 vs 객체

개념설명비유
클래스(Class)객체를 만들기 위한 설계도, 틀붕어빵 기계
객체(Object)클래스를 이용해 메모리에 만들어진 실제 데이터붕어빵
인스턴스화클래스를 기반으로 객체를 메모리에 생성하는 행위기계로 붕어빵 찍기
Student s;  // 클래스 Student를 인스턴스화 → s는 객체

// 인스턴스화 : 변수로 선언됨 → 메모리에 잡힘 → 객체
class 붕어빵틀 {
    // 생김새, 맛, 내용물 정의
};

붕어빵 s;  // s는 실제로 찍힌 붕어빵

s는 붕어빵 하나 / 클래스를 기반으로 만들어진 객체(인스턴스)
→ 그 붕어빵에 내용물 바꾸거나 한입 먹는 행위 = 멤버 접근

4️⃣ 접근제어

멤버 함수 / 멤버 변수 접근 방법 (. 연산자 사용)
: 객체.멤버이름 형태로 접근

s.getAvg();    // 함수 접근
s.kor = 100;   // 변수 접근 (public일 때만)

🔐 접근 지정자
접근 지정자 사용해 멤버의 접근 권한을 제어할 수 있음

지정자설명
public외부에서도 접근 가능
private외부 접근 불가, 클래스 내부에서는 OK
protected상속받은 클래스에서 접근 가능

접근자내 클래스내 자식 클래스외부 클래스
private
protected
public

💡 규칙

  • 접근 지정자를 명시하지 않으면 → private 이 기본값
class Student {
    int kor;     // ← 자동으로 private
    void hello(); // ← 이것도 private
};
  • 멤버 변수 private : 외부에서 직접 수정 못하게 함 → 정보 보호(캡슐화)

  • 멤버 함수 public : 외부에서 객체 동작 호출 가능하게

class Student {
public:
    double getAvg();     // 외부에서 호출할 수 있도록

private:
    int kor, eng, math;  // 외부에서 직접 접근 불가
};

5️⃣ getter & setter

🔒 private로 멤버 변수를 숨기는 이유
사용자가 직접 변수에 접근하는 건 위험함
→ 잘못된 값이 들어가거나 객체의 상태가 망가질 수 있음
→ 외부에서 직접 변수 접근을 막고,
필요할 때만 함수로 값에 접근하거나 수정하게 함

✅ getter와 setter의 역할
직접 제어하지 않고 내가 구현한 멤버 함수에 의해서만 변수를 제어할 수 있도록 하는 함수

구분설명예시 함수
setter값을 변경할 때 사용하는 함수setMathScore(int math)
getter값을 읽어올 때 사용하는 함수getMathScore()

 void setMathScore(int math)
    {
        this->math = math;
    }
    void setEngScore(int eng)
    {
        this->eng = eng;
  
    }
    void setKorScore(int kor)
    {
        this->kor = kor;
    }
  • 멤버 변수는 private로 숨기고
    → 외부에서는 getter, setter 를 통해서만 제어
  • setter를 호출해서 값을 수정함
    → 이렇게 하면 의도된 상황에서만 값이 변경됨
    → 객체를 안정적이고 일관된 상태 유지 가능

6️⃣ this

✅ this

  • 자기 자신(객체 자신)을 가리키는 포인터
  • 클래스 안에서 자기 자신을 참조할 때 사용함
class Student {
private:
    int math; // 이건 클래스의 "멤버 변수"
public:
    void setMathScore(int math) { // 이건 "함수 매개변수"
        this->math = math;	// 왼쪽 this->math는 객체의 변수, 오른쪽 math는 매개변수
    }
};
이름위치/정의설명예시
math함수 매개변수함수가 외부에서 전달받는 값 (임시 저장용)그냥 math라고 씀
this->math클래스 내부 멤버 변수객체가 실제로 가지고 있는 변수, 값을 저장하는 곳this->math로 접근

7️⃣ 매개변수 & 인자

✅ 매개변수

  • 함수에 값을 전달하기 위해 사용하는 임시 변수
  • 함수는 택배를 받는 사람이고,
    매개변수는 그 사람이 택배를 받을 상자(label)📦 같은 역할

✅ 매개변수와 인자 차이

구분설명예시 코드에서 역할
📦 매개변수함수를 선언/정의할 때 쓰는 변수int add(int a, int b)에서 a, b
🎁 인자함수를 호출할 때 넣는 실제 값add(3, 5)에서 3, 5
int add(int a, int b) {  // → a, b는 "매개변수"
    return a + b;
}

int main() {
    int result = add(3, 5); // → 3, 5는 "인자(인수)"
}
  • 인자(Argument) = 실제 물건 🎁
    → 함수 호출할 때 실제로 넣는 값

8️⃣ 생성자

객체를 생성할 때 딱 한 번 자동으로 호출되는 특별한 멤버 함수
객체 생성 이후에는 생성자 호출 불가

항목설명
이름클래스 이름과 동일해야 함
반환형없음 (void도 쓰지 않음)
자동 호출객체가 생성될 때 자동으로 딱 한 번 호출됨
목적객체가 처음 만들어질 때 멤버 변수를 초기화하거나 객체가 동작할 준비 작업을 하기 위해 사용
클래스 
  ↓ (인스턴스화)
객체 생성 (실제 메모리에 올라감)
  ↓
생성자(Constructor) 자동 호출
  ↓
멤버 변수 초기화 등 준비 작업 실행

✅ 매개변수 없는 기본 생성자

#include <iostream>
using namespace std;

// 클래스 정의
class Person {
public:
    string name;	// 멤버 변수
    int age;		// 멤버 변수

    // 기본 생성자 : 인자가 없는 생성자
    // 클래스 이름과 같은 함수 → Person()
    Person() {
        name = "Unknown";
        age = 0;
        // 반환형이 없음 → 생성자
		// 객체가 생성될 때 자동으로 실행됨
    }

	// 멤버 함수 display()
    void display() {
        cout << "Name: " << name << ", Age: " << age << endl;
    }
};

int main() {
    Person p;   // 객체 p 생성 → 생성자 자동 호출
    p.display();   // p의 정보 출력
    return 0;
}



// 실행 흐름

[클래스 Person 정의됨][main 함수 시작][Person p; → 객체 생성][생성자 Person() 자동 호출][p.name = "Unknown", p.age = 0 설정됨][p.display() 실행 → 정보 출력]

Person p;

  • 여기서 Person() 생성자가 자동으로 호출됨
  • p.name = "Unknown", p.age = 0 으로 설정됨

p.display();

  • 출력: Name: Unknown, Age: 0

✅ 매개변수 있는 생성자

  • 객체를 만들 때 초기값(name, age)을 직접 넣어줄 수 있는 생성자
  • 사용자가 원하는 값으로 초기화할 수 있게 만들어줌
#include <iostream>
using namespace std;

class Person {
public:
    string name;	// 멤버 변수
    int age;	    // 멤버 변수

    // 매개변수가 있는 생성자
    // 매개변수 n, a를 받아서 멤버 변수 name, age에 넣어줌
    Person(string n, int a) { 
        name = n;
        age = a;
    }

    void display() {
        cout << "Name: " << name << ", Age: " << age << endl;
    }
};

int main() {
    Person p("Alice", 25); // 매개변수가 있는 생성자 호출
    p.display();
    return 0;
}


// 실행 흐름

객체 생성: Person p("Alice", 25);
     ↓
매개변수 있는 생성자 호출: Person("Alice", 25)
     ↓
n → "Alice", a → 25
this->name = n ("Alice")
this->age = a (25)
     ↓
p.display();  → 화면에 출력
Person p("Alice", 25);

/*
1. Person 클래스의 객체 p를 생성
2. 매개변수가 있는 생성자 Person(string, int)가 자동 호출됨
3. name = "Alice", age = 25로 설정됨
*/

✅ 기본 매개변수가 있는 생성자

  • 인자를 전달 안 하면 기본값 사용
  • 인자를 전달하면 전달한 값으로 초기화
  • 생성자를 하나로 두 가지 역할 가능
    • 기본 생성자 + 매개변수 생성자 역할 동시에 수행
#include <iostream>
using namespace std;

class Person {
public:
    string name;
    int age;

    // 기본 매개변수가 있는 생성자
    // n, a에 기본값이 설정되어 있음
    Person(string n = "DefaultName", int a = 18) {
        name = n;
        age = a;
    }

    void display() {
        cout << "Name: " << name << ", Age: " << age << endl;
    }
};

int main() {
    Person p1;              // 기본값 사용
    Person p2("Bob", 30);   // 값을 지정
    p1.display();
    p2.display();
    return 0;
}

/*
출력 결과
Name: DefaultName, Age: 18
Name: Bob, Age: 30
*/

9️⃣ 헤더 파일 vs 소스 파일

파일 종류역할
헤더파일 .h선언(정의가 아님), 인터페이스 제공
소스파일 .cpp실제 동작 구현 (함수 정의 등)

📄 헤더 파일을 따로 만드는 이유

  • 중복 선언 방지
    • 여러 번 include 되어도 #pragma once 사용해서 딱 한 번만 읽히도록 함
  • 가독성
    • 선언 (헤더) / 구현 (소스)
  • 재사용성
    • 다른 프로젝트에서 헤더만 include 하면 사용 가능

0개의 댓글