본문 바로가기

Programming/C++ 2

[C++] 10장-3. 클래스 생성자와 파괴자

반응형

목적: 클래스 객체를 표준 데이터형 사용하듯이 사용

 

int year = 2001; //적법한 초기화
struct thing
{
	char* pn;
    int m;
};

thing amabob = {"wodget", -23}; //적법한 초기화
Stock hot={"Sukie's Autos, Inc.", 200, 50.25; //컴파일 에러

-> Stock형에 일반적인 초기화 문법 적용이 안된다.

-> private접근제어로 직접 접근 불가능 [멤버 함수를 이용해야 한다.]

 

 

클래스 생성자

:객체를 성공적으로 초기화 하기 위한 멤버 함수

즉, 새로운 객체를 생성하고 그들의 데이터 멤버에 값을 대입해주는 역할을 수행

 

생성자의 이름 = 클래스의 이름

생성자는 리턴값이 없지만 void로 선언하지도 않는다

생성자는 데이터형을 선언하지 않는다.

객체를 선언할 때 자동호출

클래스 멤버 이름을 매개변수로 사용하지 말것

객체를 만드는 일을 끝내기 전까지는 객체가 없는 상태, 생성자 호출에 객체를 사용할 수 없다.


생성자의 선언과 정의

 

 

//디폴트 매개변수를 사용하는 생성자의 원형
Stock(const std::string& co, long n =0, double pr =0.0);

-> const std::string& co : 문자열을 지시하는 포인터, company 문자 배열 멤버를 초기화

long n , double pr: shares와 share_val 멤버를 위한 값

 

-> 리턴형이 없다

Stock::Stock(const std::string& co, long n, double pr)
{
	std::cout << co << "를 사용하는 생성자가 호출되었습니다." << std:: endl;
	company= co;

	if (n < 0)
	{
		std::cerr << "주식 수는 음수가 될 수 없으므로, "
			<< company << " shares를 0으로 설정합니다" << std::endl;
		shares = 0;
	}
	else
		shares = n;
	share_val = pr;
	set_tot();
}

-> acqurie() 함수에 사용한 것과 같은 코드

-> 이 경우는 객체를 선언할 때 프로그램이 자동으로 생성자를 호출

 


생성자 사용하기

-> 객체를 초기화 하는 2가지 방법

Stock food =Stock("World Cabbage", 250, 1.25); //명시적 호출
Stock garment("Furry Mason", 50, 2.5); //암시적 호출

Stock garment=Stock("Furry Mason", 50, 2.5); //2번을 명시적 호출

new를 사용할 때 생성자

Stock* pstock = new Stock("Electroshock Games", 18, 19.0);

-> Stock 객체를 생성, 매개변수를 통해 제공되는 값으로 그것을 초기화

-> 그 객체의 주소를 pstock 포인터를 대입 [이 경우엔 객체가 이름이 없지만 포인터로 객체에 접근 가능-> 11장]

-> 객체를 만드는 일을 끝내기 전까지는 객체가 없는 상태, 생성자 호출에 객체를 사용할 수 없다

 


디폴트 생성자

-> 명시적인 초기화 값을 제공하지 않을 때 객체를 생성할 때 사용하는 생성자

 

Stock fluffy_the_cat; //디폴트 생성자를 사용
Stock::Stock() { } //디폴트 생성자 생성

 

Stock::Stock(const std::string& co, long n, double pr)
Stock stock1; //생성자가 있는데 디폴트생성자가 없으면 에러 발생

 

 

디폴트 생성자의 2가지 정의 방법

Stock::Stock(const std::string& co="Error", long n=0, double pr=0.0);

// 기존의 생성자에 있는 모든 매개변수에 디폴트 값을 제공
Stock();
//함수 오버로딩을 사용하여 매개변수가 없는 또 하나의 생성자를 정의

-> 하나의 디폴트 생성자만 존재해야 한다.

 

Stock::Stock() //디폴트 생성자
{
	company="no name";
    shares=0;
    share_val=0.0;
    total_val=0.0;
}

 

Stock first; //디폴트 생성자를 암시적 호출
Stock first =Stock(); // 명시적 호출
Stock* prelief = new Stock; //암시적 호출

 

Stock first("Concrete Conglomerate"); //생성자 호출
Stock second(); //함수를 선언
Stock third; //디폴트 생성자를 암시적 호출

-> 디폴트가 아닌 생성자에는 암시적 호출은 불가 [디폴트 생성자의 암시적 호출에는 괄호를 사용하면 안된다.]

 


파괴자

: 생성자를 사용할 때 객체가 끝날 때까지 추적을 해야한다. 객체가 끝날 때 파괴자를 호출

 

Stock::~Stock()	//클래스 파괴자

{
	cout<<"안녕, "<<company<<"!\n";
}

파괴자 호출 시점

1. 프로그램이 종료될 때

2. delete를 사용할 때

3. 임시객체 생성하고 임시 객체 사용이 끝날 때

 


//stock10.h

#ifndef STOCK10_H_
#define STOCK10_H_

#include <string>

class Stock
{
private:
	std::string company;
	long shares;
	double share_val;
	double total_val;
	void set_tot() { total_val = shares * share_val;}
public:
	//두 개의 생성자
	Stock(); //디폴트 생성자
	Stock(const std::string& co, long n = 0, double pr = 0.0);
	~Stock(); //파괴자

	void buy(long num, double price);
	void sell(long num, double price);
	void update(double price);
	void show();
};

#endif STOCK10_H_
//stock10.cpp
#include <iostream>
#include "stock10.h"

//생성자들
Stock::Stock() //디폴트 생성자
{
	std::cout << "디폴트 생성자가 호출되었습니다. " <<std::endl;
	company = "no name";
	shares = 0;
	share_val = 0.0;
	total_val = 0.0;
}

Stock::Stock(const std::string& co, long n, double pr)
{
	std::cout << co << "를 사용하는 생성자가 호출되었습니다." << std:: endl;
	company= co;

	if (n < 0)
	{
		std::cerr << "주식 수는 음수가 될 수 없으므로, "
			<< company << " shares를 0으로 설정합니다" << std::endl;
		shares = 0;
	}
	else
		shares = n;
	share_val = pr;
	set_tot();
}
//클래스 파괴자
Stock::~Stock() //메시지를 출력하는 클래스 파괴자
{
	std::cout << "안녕," << company << "!\n";
}

//다른 메서드들
void Stock::buy(long num, double price) //기존 보유 주식의 증가와 감소 관리
{
	if (num < 0)
	{
		std::cout << "매입 주식 수는 음수가 될 수 없으므로, "
			<< "거래가 취소되었습니다.\n";
	}
	else
	{
		shares += num;
		share_val = price;
		set_tot();
	}
}
void Stock::sell(long num, double price) //기존 보유 주식의 감소 관리
{
	using std::cout;
	if (num < 0)
	{
		cout << "모두 주식 수는 음수가 될 수 없으므로, "
			<< "거래가 취소되었습니다. \n";
	}
	else if (num > shares)
	{
		cout << "보유 주식보다 많은 주식을 매도할 수 없으므로, "
			<< "거래가 취소되었습니다. \n";
	}
	else
	{
		shares -= num;
		share_val = price;
		set_tot();
	}
}
void Stock::update(double price)
{
	share_val = price;
	set_tot();
}

//void Stock::show()
//{
//	std::cout << "회사명: " << company
//		<< " 주식 수: " << shares << '\n'
//		<< " 주가: $" << share_val
//		<< " 주식 총 가치: $" << total_val << '\n';
//}

void Stock::show()
{
	using std::cout;
	using std::ios_base;//fmtflags가 ios_base 클래스, std 이름공간에서 정의
	//set format #.##
	ios_base::fmtflags orig = //orig가 긴 타입 이름이 된다.
		cout.setf(ios_base::fixed, ios_base::floatfield);
	std::streamsize prec = cout.precision(2);

	cout << "회사명: " << company
		<< " 주식 수: " << shares << '\n';
	cout << " 주가: $" << share_val;
	//set format to #.##
	cout.precision(2);
	cout << " 주식 총 가치: $" << total_val << '\n';

	//restore original format
	cout.setf(orig, ios_base::floatfield);
	cout.precision(prec);
}
//usestok1.cpp
#include <iostream>
#include "stock10.h"

int main()
{
	using std::cout;
	cout << "생성자를  사용하여 새로운 객체들을 생성한다. \n";
	Stock stock1("NanoSmart", 20, 12.50); //구문 1
	stock1.show();
	stock1.buy(15, 18.125);

	Stock stock2 = Stock("Boffo Objects", 2, 2.0); //구문 2
	stock2.show();

	cout << "stock1을 stock2에 대입한다 \n";
	stock2 = stock1;

	cout << "sotck1과 stock2를 출력한다.\n";
	stock1.show();
	stock2.show();

	cout << "생성자를 사용하여 객체를 재설정한다. \n";
	stock1 = Stock("Nifty Foods", 10, 50.0); //임시 객체
	cout << "갱신된 stock1: \n";
	stock1.show();
	cout << "프로그램을 종료합니다. \n";
	return 0;
}

Stock stock2= Stock("Boffo Objects", 2, 2.0);

stock1 = Stock("Nifty Foods", 10, 50.0); //임시 객체

-> 첫 번째는 초기화를 호출하고 객체를 생성한다 임시객체를 생성할 수도 안할수도 있다

 

->두번째는 대입을 호출한다. 대입 구문에서 이런 방식으로 생성자를 사용하면 대입 전 항상 임시객체를 생성한다.

반응형

'Programming > C++ 2' 카테고리의 다른 글

[C++] 10장-5. 객체 배열  (0) 2021.04.11
[C++] 10장-4. this 포인터  (0) 2021.04.11
[C++] 10장-2. 추상화와 클래스  (0) 2021.04.11
[C++ 요약] 3장. 데이터 처리  (0) 2021.04.09
[C++] 3장-4. C++ 산술 연산자  (0) 2021.04.09