본문 바로가기
카테고리 없음

클래스와 생성자

by 가보자곳 2025. 6. 7.
반응형

클래스

클래스란 곧 타입이다.

그리고 객체는 곧 인스턴스이다.

타입을 바탕으로 찍어내는 구조이다.

 

데이터 멤버객체가 갖는 속성이고

멤버 함수객체가 갖는 행위이다.

 

그럼 객체지향 언어 C++에서 클래스를 어떻게 사용하는가?

 

클래스 정의

우선 클래스를 정의한다. 이때 속성(데이터 멤버)과 행위(멤버 함수)를 선언한다.

 

예를 들어

class Circle
{
private:
	double radius;
public:
	double getRadius() const;
	double getArea() const;
	double getPerimeter() const;
	void setRadius(double value);
};

클래스 Circle을 정의하고
radius라는 속성과
getRadius(), getArea(), getPerimeter(), setRadius()라는 행위를 정의한 것이다.

 

멤버함수 정의

그 다음은 멤버 함수 정의를 해야한다. 이때 위에서 선언한 각 행위의 세부사항들을 정의하는 것이다.

 

double Circle::getRadius() const
{
	return radius;
}

double Circle::getArea() const
{
	const double PI = 3.14;
	return (PI * radius * radius);
}

double Circle::getPerimeter() const
{
	const double PI = 3.14;
	return (2 * PI * radius);
}

void Circle::setRadius(double value)
{
	radius = value;
}

getRadius(), getArea(), getPerimeter(), setRadius()라는 위에서 정의한 행위들이 무엇을 하는건지 세세하게 적은 것이다.

 

애플리케이션

마지막으로 애플리케이션이다.

객체를 인스턴스화하고 사용하는 단계이다.

대체로 main함수 부분이다.

int main()
{
	//Circle 객체를 Circle1로 인스턴스화
	cout << "Circle 1: " << endl;
	Circle circle1;
	circle1.setRadius(10.0);
	cout << "Radius: " << circle1.getRadius() << endl;
	cout << "Area: " << circle1.getArea() << endl;
	cout << "Perimeter: " << circle1.getPerimeter() << endl << endl;

	//Circle 객체를 Circle2로 인스턴스화
	cout << "Circle 2: " << endl;
	Circle circle2;
	circle2.setRadius(20.0);
	cout << "Radius: " << circle2.getRadius() << endl;
	cout << "Area: " << circle2.getArea() << endl;
	cout << "Perimeter: " << circle2.getPerimeter();
	return 0;
}

 

이러면 결과가

Run:
Circle 1:
Radius: 10
Area: 314
Perimeter: 62.8
Circle 2:
Radius: 20
Area: 1256
Perimeter: 125.6

와 같이 나오게 된다.

 

큰 틀은 위와 같다.

 

이제 접근 제한자에 대해 알아보겠다.

접근 제한자 같은 클래스에서의 접근 서브 클래스에서의 접근 모든 곳으로부터의 접근
private 가능 불가능 불가능
potected 가능 가능 불가능
public 가능 가능 가능

접근 제한자로는 private, potected, public이 있다.

 

데이터 멤버(속성)에는 일반적으로 private이 쓰인다.

기본적으로 private이 붙긴 하지만 그래도 private을 써주는 것을 습관화 하자.

private이 적용된 데이터 멤버(속성)은 "멤버 함수를 통해서만" 접근이 가능하다.

그냥 바로 건들 수가 없다는 것이다.

 

멤버 함수는 일반적으로 public을 쓴다. 

클래스 정의 부분에서 멤버 함수는 단순하게 선언만 된다.

세부 내용은 클래스 정의가 끝난 후 '멤버 함수 정의'를 통해 이루어진다.

 

멤버 함수 정의를 클래스 정의랑 한 번에 할 수 없나요?

가능하다. 

실제로 멤버 함수 정의를 클래스 정의 이후에 진행하면

이 함수가 어디서 온 건지 알려줘야 해서 앞에 클래스 이름을 붙여 줘야 된다.

double Circle :: getPerimeter() const
{
	const double PI = 3.14;
	return(2 * PI * radius);
}

 

 

그리고 인라인 함수로 작성하면 이를 한 번에 진행 할 수 있다.

class Circle
{
	// 데이터 멤버 정의
	private:
	double radius;
	// 멤버 함수 정의
	public:
	double getRadius()const {return radius };
};

보시는 것처럼 클래스 정의 부분에서 멤버 함수를 선언할 때 함수의 기능까지 삽입한다.

 

이렇게 정의된 클래스를 바탕으로 이제 main 함수에서

객체 인스턴스화

Circle circle1;

 

멤버 선택(멤버함수를 .으로 접근!)

circle1.setRadius(10.0);
cout << "Radius: " << circle1.getRadius() << endl;
cout << "Area: " << circle1.getArea() << endl;
cout << "Perimeter: " << circle1.getPerimeter() << endl << endl;

을 진행해주면 된다.

 

 

이것이 클래스의 기본 개념이다.

이제는 생성자에 대해 알아보겠다.

 

생성자

생성자는 객체를 생성하는 특별한 '멤버 함수'이다.

생성자 내부에서 객체의 데이터 멤버를 초기화 하며

리턴값이 없고, 이름이 클래스 이름과 동일하다.

그냥 관례라고 생각하면 편하다.

 

이 생성자는 크게 

  • 매개 변수가 있는 생성자
  • 기본 생성자
  • 복사 생성자

로 구분된다.

 

이 또한 멤버 함수이므로

클래스 정의에서 정의를 해주어야 한다.

class Circle
{
	public:
	Circle(double radius); // 매개변수가 있는 생성자
	Circle(); // 기본 생성자
	Circle(const Circle& circle); // 복사 생성자
}

 

매개 변수가 있는 생성자 (parameter constructor)

Circle :: Circle (double rds)
: radius(rds) // 초기화
{
	// 추가 실행 코드
}

일반적으로 데이터 멤버를 지정된 값으로 초기화하기 위해서 매개변수가 있는 생성자 를 사용

 

기본 생성자(default constructor)

Circle :: Circle()
: radius(0.0) // 초기화
{
	// 추가 실행 코드
}

기본 생성자는 매개변수가 없는 생성자를 의미

 

복사 생성자(copy constructor)

Circle :: Circle(const Circle& cr)
: radius(cr.radius) // 초기화
{
	// 추가 실행 코드
}

복사 생성자로 객체를 복사하면 원본과 복사된 사본이 같은 값을 갖는 다른 객체로 생성

 

 

소멸자

생성자가 있으면 소멸자도 있어야 한다.

소멸자 역시 멤버 함수이므로 클래스 정의에 선언 돼있어야 하며 

class Circle
{
	public:
		~Circle(); // 소멸자 선언
}

생성자와 마찬가지로 리턴값을 가질 수 없다.

 

소멸자의 종류는 한 종류밖에 없다.

Circle :: ~Circle()
{
    // 추가 실행 코드
}

이름 앞에 물결 기호가 있다

 

소멸자는 객체가 스코프를 벗어나는 등의 상황에서 자동적으로 호출되므로 

꼭 써줄 필요는 없으나 써주는 습관을 기르자

 

위에서 설명한 생성자와 소멸자는 써주지 않아도

필요한 경우 시스템에서 자동으로 생성한다.

그러나 데이터 멤버가 쓰레기 값으로 초기화 되는 등

원하지 않는 상황이 발생할 수 있으니

직접 구현하는 습관을 기르자

 

그럼 아래는 완성 코드이다

 

#include <iostream>
using namespace std;

//*******************************************************************

class Circle
{
private:
    double radius;	//속성
public:
    Circle(double radius); // 매개변수 생성자
    Circle(); // 기본 생성자
    ~Circle(); // 소멸자
    Circle(const Circle& circle); // 복사 생성자
    void setRadius(double radius); // 설정자 (Mutator)
    double getRadius() const; // 접근자 (Accessor)
    double getArea() const; // 접근자 (Accessor)
    double getPerimeter() const; // 접근자 (Accessor)
};

//*******************************************************************

// 매개변수 생성자의 정의
Circle::Circle(double rds)
: radius(rds)
{
    cout << "매개변수 생성자가 호출되었습니다." << endl;
}

// 기본 생성자의 정의
Circle::Circle()
: radius(0.0)
{
    cout << "기본 생성자가 호출되었습니다." << endl;
}

// 복사 생성자의 정의
Circle::Circle(const Circle& circle)
: radius(circle.radius)
{
    cout << "복사 생성자가 호출되었습니다." << endl;
}

// 소멸자의 정의
Circle::~Circle()
{
    cout << "반지름이 " << radius << "인 원의 소멸자가 호출되었습니다." << endl;
}

// setRadius 멤버 함수 정의
void Circle::setRadius(double value)
{
    radius = value;
}

// getRadius 멤버 함수 정의
double Circle::getRadius() const
{
    return radius;
}

// getArea 멤버 함수 정의
double Circle::getArea() const
{
    const double PI = 3.14;
    return (PI * radius * radius);
}

// getPerimeter 멤버 함수 정의
double Circle::getPerimeter() const
{
    const double PI = 3.14;
    return (2 * PI * radius);
}

//*******************************************************************

int main()
{
    // circle1 인스턴스 생성 및 연산 수행
    Circle circle1(5.2);
    cout << "반지름: " << circle1.getRadius() << endl;
    cout << "넓이: " << circle1.getArea() << endl;
    cout << "둘레: " << circle1.getPerimeter() << endl << endl;

    // circle2 인스턴스 생성 (복사 생성자 사용) 및 연산 수행
    Circle circle2(circle1);
    cout << "반지름: " << circle2.getRadius() << endl;
    cout << "넓이: " << circle2.getArea() << endl;
    cout << "둘레: " << circle2.getPerimeter() << endl << endl;

    // circle3 인스턴스 생성 (기본 생성자 사용) 및 연산 수행
    Circle circle3;
    cout << "반지름: " << circle3.getRadius() << endl;
    cout << "넓이: " << circle3.getArea() << endl;
    cout << "둘레: " << circle3.getPerimeter() << endl << endl;

    // 소멸자는 여기서 자동 호출됨
    return 0;
}

 

결과는 아래와 같이 나온다
코드를 한 줄 한 줄 따라가며 비교해봐라

 

실행 결과:

매개변수 생성자가 호출되었습니다.
반지름: 5.2
넓이: 84.9056
둘레: 32.656

복사 생성자가 호출되었습니다.
반지름: 5.2
넓이: 84.9056
둘레: 32.656

기본 생성자가 호출되었습니다.
반지름: 0
넓이: 0
둘레: 0

반지름이 0인 원의 소멸자가 호출되었습니다.
반지름이 5.2인 원의 소멸자가 호출되었습니다.
반지름이 5.2인 원의 소멸자가 호출되었습니다.

 

반응형

댓글