🥞 BE
home

5. 클래스와 생성자 (1)

Date
2022/05/03
Category
Programming Language
Tag
C++
Detail
타입과 인스턴스
타입(type)을 이용해 인스턴스(instance)를 생성할 수 있음.
타입은 추상화된 것이고 인스턴스는 타입을 실체화한 구체적인 실체.
타입과 인스턴스는 일대다(one-to-many) 관계.
속성과 행위
속성(attribute)은 인스턴스가 가지는 특징을 의미함.
행위(behavior)는 어떤 인스턴스가 스스로 할 수 있는 작업 또는 연산을 말함.
클래스와 객체
C++은 클래스라는 구문을 사용하여 타입(사용자 정의 자료형)을 생성.
이러한 클래스를 기반으로 만든 인스턴스를 객체라고 부름.
데이터 멤버
객체의 데이터 멤버는 속성을 표현하기 위한 변수를 의미함.
객체의 행위는 멤버 함수를 사용해서 구현함.
3가지 시스템
새로운 자료형(타입)을 만들 때는 클래스를 사용함.
1. 클래스 정의 - 속성과 행위 선언
2. 멤버 함수 정의 - 행위 정의
3. 애플리케이션 - 클래스를 기반으로 객체를 인스턴스화하고 사용
3가지 시스템으로 분리됨.
1. 클래스 정의
1-1. 데이터 멤버 선언 (접근제한자 : private)
1-2. 멤버 함수 선언 (접근제한자 : public)
→ 접근 제한자는 그룹 단위로 붙임.
2. 멤버 함수 정의
ex)
자료형 클래스명::멤버함수명()const{
~
리턴 값
}
3. 애플리케이션으로 활용 (객체 인스턴스화)
ex)
클래스명 객체명;
객체명.멤버함수명(데이터 멤버 값)
→ 클래스와 멤버 함수를 인스턴스화하여 사용
Circle 객체 만들고 사용하기
#include<iostream> using namespace std; // const - 변수의 값이 바뀌는 것을 막기 위한 한정사. (상수 취급) // :: - 스코프 기호 // 클래스 정의 class Circle { private: double radius; public: double getRadius()const; double getArea()const; double getPerimeter()const; void setRadius(double value); }; // 멤버 함수 정의 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; } // 애플리케이션 (객체 인스턴스화) int main() { cout << "Circle 1: " << endl; Circle circle1; // circl1이라는 객체로 Circle 클래스를 불러와서 인스턴스화 시킴. circle1.setRadius(10.0); cout << "Radius: " << circle1.getRadius() << endl; cout << "Area:" << circle1.getArea() << endl; cout << "Perimeter: " << circle1.getPerimeter() << endl << endl; cout << "Circle 2: " << endl; Circle circle2; circle2.setRadius(20.0); cout << "Radius: " << circle2.getRadius() << endl; cout << "Area: " << circle2.getArea() << endl; cout << "Perimeter: " << circle2.getPerimeter() << endl; return 0; }
C++
복사
접근 제한자
접근 제한자
같은 클래스에서의 접근
서브 클래스에서의 접근
모든 곳으로부터의 접근
private
가능
불가능
불가능
protected
가능
가능
불가능
public
가능
가능
가능
데이터 멤버에는 일반적으로 private를 적용.
멤버 선택자
객체의 이름과 멤버 함수 사이에 점(.)을 찍어 사용함.
이 연산자를 사용하면 어떤 객체가 갖고 있는 어떤 함수를 실행할 수 있음.
생성자와 소멸자
객체가 데이터 멤버를 갖고 어떤 작업을 하려면, 객체를 만든 뒤 데이터 멤버를 초기화하는 작업이 필요함.
객체는 생성자(constructor)라고 부르는 특별한 멤버 함수가 호출될 때 생성됨. 따라서 생성자 내부에서 초기화를 하면 편리.
객체가 더 이상 필요가 없어지는 경우, 객체가 차지하고 있는 메모리를 비워줘야 함(메모리 재활용).
이때는 소멸자(destructor)라고 부르는 특별한 멤버 함수가 호출되어 소멸자 내부에서 객체를 정리하는 작업을 함.
생성자
1.
리턴 값이 없음.
2.
이름이 클래스의 이름과 같음.
3.
매개변수가 있는 생성자, 기본 생성자, 복사 생성자 3가지로 구분됨.
소멸자
1.
소멸자의 이름은 클래스 이름 앞에 물결 기호(~)가 붙은 형태
2.
소멸자는 리턴 값을 가질 수 없음
필수 멤버 함수
그룹 1 : 매개변수가 있는 생성자 / 기본 생성자
그룹 2 : 복사 생성자
그룹 3 : 소멸자
그룹 1은 둘 중 하나의 생성자라도 있어야 함.
둘 다 만들지 않으면, 시스템은 합성 기본 생성자를 생성하여 데이터 멤버를 쓰레기 값으로 초기화함.
그룹 2도 동일.
만들지 않으면, 합성 복사 생성자를 생성.
그룹 3도 동일.
만들지 않으면, 합성 소멸자를 생성.
Circle 클래스 완성
#include<iostream> using namespace std; // const : 변수의 값이 바뀌는 것을 막기 위한 한정사. (상수 취급) // 클래스 정의 class Circle { private: double radius; public: Circle(double radius); // 매개변수 생성자 Circle(); // 기본 생성자 ~Circle(); // 소멸자 Circle(const Circle& circle); // 복사 생성자 double getRadius()const; double getArea()const; double getPerimeter()const; void setRadius(double value); }; // 매개변수 생성자 정의 Circle::Circle(double rds) : radius(rds) { cout << "The parameter constructor was called. " << endl; } // 기본 생성자 정의 Circle::Circle() : radius(0.0) { cout << "The default constructor was called. " << endl; } // 복사 생성자 정의 Circle::Circle(const Circle& circle) :radius(circle.radius) { cout << "The copy constructor was called. " << endl; } // 소멸자 정의 Circle::~Circle() { cout << "The destructor was called for circle with radius: " << endl; } // 멤버 함수 정의 void Circle::setRadius(double value) { radius = value; } 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); } // 애플리케이션 (객체 인스턴스화) int main() { Circle circle1(5.2); cout << "Radius: " << circle1.getRadius() << endl; cout << "Area:" << circle1.getArea() << endl; cout << "Perimeter: " << circle1.getPerimeter() << endl << endl; Circle circle2(circle1); cout << "Radius: " << circle2.getRadius() << endl; cout << "Area: " << circle2.getArea() << endl; cout << "Perimeter: " << circle2.getPerimeter() << endl << endl; Circle circle3; cout << "Radius: " << circle3.getRadius() << endl; cout << "Area: " << circle3.getArea() << endl; cout << "Perimeter: " << circle3.getPerimeter() << endl << endl; return 0; }
C++
복사
3개의 랜덤한 숫자 클래스 정의하고 만들기
#include<iostream> #include<cstdlib> #include<ctime> using namespace std; // 데이터 멤버 및 멤버 함수 선언 class RandomInteger { private: int low; int high; int value; public: RandomInteger(int low, int high); // 매개변수 생성자 ~RandomInteger(); // 소멸자 RandomInteger(const RandomInteger& random) = delete; // 복사 생성자 void print()const; // 멤버 함수 }; // 생성자 정의 RandomInteger::RandomInteger(int lw, int hw) :low(lw), high(hw) { srand(time(0)); int temp = rand(); value = temp % (high - low + 1) + low; // 값 범위 지정 } // 소멸자 정의 RandomInteger::~RandomInteger() { } // 멤버 함수 void RandomInteger::print()const { cout << value << endl; } // 애플리케이션 int main() { RandomInteger r1(100, 200); cout << "Random number between 100 and 200: "; r1.print(); RandomInteger r2(400, 600); cout << "Random number between 400 and 600: "; r2.print(); RandomInteger r3(1500, 2000); cout << "Random number between 1500 and 2000: "; r3.print(); return 0; }
C++
복사
인스턴스 멤버 함수 선택자
어떻게 어떤 객체가 함수를 사용하고 있을 때 잠그고, 모두 사용한 뒤에 다른 객체가 사용할 수 있도록 잠금을 해제할 수 있는 것일까?
→ 락킹과 언락킹을 사용
이를 위해서 멤버 함수에 this 포인터(객체의 주소를 나타내는 변수)를 지정하는 방법을 사용함.
모든 멤버 함수에는 이와 같은 this 포인터가 숨겨져 있음.
(이건 몇 번을 봐도 이해가 안 된다... 코드 이해는 되는데 왜 쓰는지를 이해 못 함)
클래스 불변 속성
"객체를 생성하는 인스턴스 멤버 함수 (매개변수가 있는 생성자)" 또는 "데이터 멤버의 값을 변경하는 설정자 멤버 함수"를 사용해서 클래스의 불변 속성을 적용.
#include<iostream> #include<cassert> using namespace std; class Rectangle { private: double length; double height; public: Rectangle(double length, double height); Rectangle(const Rectangle& rect); ~Rectangle(); void print()const; double getArea()const; double getPerimeter()const; }; Rectangle::Rectangle(double len, double hgt) :length(len), height(hgt) { if ((length <= 0.0) || (height <= 0.0)) { cout << "No rectangle can be made!" << endl; assert(false); // 에러검출용 코드. false가 들어오면 assert error! } } Rectangle::Rectangle(const Rectangle& rect) :length(rect.length), height(rect.height) { } Rectangle::~Rectangle() { } void Rectangle::print()const { cout << "A rectangle of " << length << " by " << height << endl; } double Rectangle::getArea()const { return(length * height); } double Rectangle::getPerimeter()const { return(2 * (length + height)); } int main() { Rectangle rect1(3.0, 4.2); Rectangle rect2(5.1, 10.2); Rectangle rect3(rect2); cout << "Rectangle 1: "; rect1.print(); cout << "Area: " << rect1.getArea() << endl; cout << "Perimeter: " << rect1.getPerimeter() << endl << endl; cout << "Rectangle 2: "; rect2.print(); cout << "Area: " << rect2.getArea() << endl; cout << "Perimeter: " << rect2.getPerimeter() << endl << endl; cout << "Rectangle 3: "; rect3.print(); cout << "Area: " << rect3.getArea() << endl; cout << "Perimeter: " << rect3.getPerimeter() << endl << endl; return 0; }
C++
복사
정적 데이터 멤버
정적 데이터 멤버는 클래스 정의에서 선언해야 하고 static이라는 키워드를 붙임.
정적 데이터 멤버 초기화
정적 데이터 멤버는 클래스 정의 후에 초기화해야 하며, 따라서 프로그램의 전역 영역에서 초기화함.
값을 초기화할 때는 클래스 이름과 클래스 스코프 연산자(::)를 추가해서 클래스에 속한다는 것을 나타내야 하며, static을 추가하면 안 됨.
정적 멤버 함수
정적 멤버 함수도 static 키워드 붙여서 선언 후 인스턴스 멤버 함수처럼 클래스 외부에서 정의.
인스턴스 또는 클래스를 통해서 호출할 수 있음.
#include<iostream> using namespace std; class Rectangle { // 클래스 정의 private: // 데이터 멤버 선언 double length; double height; static int count; // 정적 데이터 멤버 public: // 멤버 함수 선언 Rectangle(double length, double height); // 매개변수 생성자 Rectangle(); // 일반 생성자 ~Rectangle(); // 소멸자 Rectangle(const Rectangle& rect); // 복사 생성자 static int getCount(); // 정적 멤버 함수 }; // 정적 데이터 멤버 초기화 int Rectangle::count = 0; // 매개변수 생성자 정의 Rectangle::Rectangle(double len, double hgt) :length(len), height(hgt) { count++; } // 일반 생성자 정의 Rectangle::Rectangle() : length(0.0), height(0.0) { count++; } // 복사 생성자 정의 Rectangle::Rectangle(const Rectangle& rect) : length(rect.length), height(rect.height) { count++; } // 소멸자 정의 -> 정적 데이터 멤버 후위감소 시킴 Rectangle::~Rectangle() { count--; } // 정적 멤버 함수 정의 int Rectangle::getCount() { return count; } // 애플리케이션 -> 객체 인스턴스화 int main() { { Rectangle rect1(3.2, 1.2); // 0++ Rectangle rect2(1.5, 2.1); // -> 1++ Rectangle rect3; // -> 2++ Rectangle rect4(rect1); // -> 3++ Rectangle rect5(rect2); // -> 4++ -> 5 cout << "Count of objects: " << rect5.getCount() << endl; } cout << "Count of objects: " << Rectangle::getCount() << endl; return 0; }
C++
복사
은행 계좌 클래스
#include<iostream> #include<cassert> using namespace std; class Account { private: long accNumber; double balance; static int base; // 정적 데이터 멤버 public: Account(double bal); // 매개변수 생성자 ~Account(); // 소멸자 void checkBalance()const; // 잔액 확인 멤버 함수 void deposit(double amount); // 보증금 멤버 함수 void withdraw(double amount); // 출금 멤버 함수 }; int Account::base = 0; Account::Account(double bal) :balance(bal) { if (bal < 0.0) { cout << "잔액이 맞지 않습니다. 프로그램을 종료합니다."; assert(false); // false 뜨면 assert error } base++; accNumber = 100000 + base; cout << "계좌 " << accNumber << " 이(가) 열렸습니다." << endl; cout << "잔액 $" << balance << endl << endl; } Account::~Account() { cout << "계좌 #: " << accNumber << " 이(가) 닫혔습니다." << endl; cout << "$" << balance << " 이(가) 고객에게 송금되었습니다." << endl << endl; } void Account::checkBalance()const { cout << "계좌 #: " << accNumber << endl; cout << "거래: 잔액 확인" << endl; cout << "잔액: $" << balance << endl << endl; } void Account::deposit(double amount) { if (amount > 0.0) { balance += amount; cout << "계좌 #: " << accNumber << endl; cout << "거래: 보증금 $" << amount << endl; cout << "새로운 잔액: $" << balance << endl << endl; } else { cout << "거래 실패." << endl; } } void Account::withdraw(double amount) { if (amount > balance) { amount = balance; } balance -= amount; cout << "계좌 #: " << accNumber << endl; cout << "거래: 출금 $: " << amount << endl; cout << "새로운 잔액: $" << balance << endl << endl; } int main() { // 3개의 계좌 생성 Account acc1(2000); Account acc2(5000); Account acc3(1000); // 거래 acc1.deposit(150); acc2.checkBalance(); acc1.checkBalance(); acc3.withdraw(800); acc1.withdraw(1000); acc2.deposit(120); return 0; }
C++
복사