190620(3일차) - dynamic_cast, static_cast, const_cast, reinterpret_cast
교육을 받으면서 노트필기 했던 내용을 날것 그대로 업로드합니다.
#include <iostream>
using namespace std;
class AAA {
int a;
protected:
int b;
public:
int c;
};
class BBB :public AAA { // private(모든멤버가 private가 되어 상속됨)
//| protected 상속 (private를 제외한 모든 멤머가 protected가 됨.)
//기본적으로는 public 상속이됩니다.
public:
int get_b(void) {
return b;
}
};
int main() {
BBB obj;
cout << obj.get_b() << endl;
cout << obj.c << endl;
return 0;
}
- 가상함수 : 실제 객체 타입을 찾아서 해당하는 함수를 사용하는 것. 메세지 다형성을 보장하기 위한 것이다. virtual 키워드를 사용한다.
#include <iostream>
using namespace std;
class AAA {
...
public:
virtual void print() {
cout << "Print" << endl;
}
};
class BBB :public AAA { ...
public:
...
void print() {
cout << "Print2" << endl;
}
};
int main() {
AAA* a = new BBB();
a->print(); //print2 호출
return 0;
}
- static_case<타입>(대상) : 컴파일 시점에 발생하는 에러를 잡아준며 형변환을 허용하도록 한다. (ex. 부모<->자식)
- 가상 소멸자 - 알맞게 메모리 해제를 하기위해 가상 소멸자를 사용한다.
class AAA {
int a;
protected:
int b;
public:
int c;
virtual void print() {
cout << "Print" << endl;
}
virtual ~AAA(); // 가상 소멸자
};
- 추상 클래스 :
virtual void print() = 0;
몸체 자체를 0으로 표현한다. (순수 가상함수)
#include <iostream>
#include <string>
using namespace std;
class Engine {
private:
int hp; // 120~290
public:
Engine(int hp=120) {
if (hp < 120)
hp = 120;
else if (hp > 290)
hp = 290;
this->hp = hp;
}
void printHp()const {
cout << "hp:" << hp << endl;
}
void setHp(int value) { hp = value; }
int getHp() const { return hp; }
};
class intercoolerEngine :Engine {
string cooler; //오일, 물
public:
intercoolerEngine() {}
intercoolerEngine(int hp=0, string cooler=""):Engine(hp){
this->cooler = cooler;
}
intercoolerEngine(intercoolerEngine& ie) {
cooler = ie.cooler;
setHp(ie.getHp());
}
void printHp()const { // 외부에서 조상 접근하기 위해 정의
Engine::printHp();
}
void printHpCooler() const{
cout << "hp:" << Engine::getHp() << ", cooler:" << cooler << endl;
}
int getHp() const{ return Engine::getHp(); } // 외부에서 조상 접근하기 위해 정의
string getCooler() const{ return cooler; }
//const intercoolerEngine* returnIntercoolerEngine()const { return this; }
};
class turboEngine : intercoolerEngine {
char turborchager; // A,B,C
public:
/*turboEngine() {}*/
turboEngine(char chager, int hp = 0, string cooler = ""):intercoolerEngine(hp,cooler){
turborchager = chager;
}
void printChargerHpCooler() const {
cout << "hp:" << getHp() <<", cooler:" << getCooler() <<", chager:"<<turborchager<< endl;
}
void printHp()const {
intercoolerEngine::printHp(); // 외부에서 조상 접근하기 위해 정의
}
void printHpCooler()const {
intercoolerEngine::printHpCooler();
}
};
int main() {
Engine eng1(150);
eng1.printHp();
intercoolerEngine eng2(270,"물");
eng2.printHpCooler();
intercoolerEngine eng3(eng2); // 복사 생성자로 복사후 출력
eng3.printHpCooler();
turboEngine eng5('B', 290, "오일");
eng5.printHp();
eng5.printHpCooler();
eng5.printChargerHpCooler();
return 0;
}
- 복사생성자 : 초기화시, 대입시, 함수콜시, 반환시 사용된다.
- 가상상속 : 중복이 되는 클래스는 단 한 번만 상속하겠다. (다중 상속의 문제점을 해결한다.)
- RTTI : type_info, dynamic_cast,
dynamic_cast<Car*>(&b) : 소괄호를 꺽쇠 형태로 변환. (ex.상속관계에서 사용) , 런타임 시점에 진행된다.
(C++ 에서의 형변환 연산)
- dynamic_cast<T>(expr) : 상속관계에서의 안전한 형 변환. T는 객체의 포인터 또는 참조형이어야 한다. 적절하지 않는 경우 컴파일시 에러가 발생한다. 자식(더많은기능) 을 부모(적은기능)에 케스팅할 때 가능하다. 컴파일시간이 아닌 실행시간에 안전성을 검사하도록 컴파일러가 바이너리 코드를 생성한다.
- bad_cast 에러 : 포인터형이아닌 참조형에 잘못된 dynamic_cast연산을 하게되면 NULL값을 참조형에 대입할 수 없기 때문에 bad_cast 에러가 발생한다.
- static_cast : dynamic_cast 보다는 좀더 강력(만능)하다. 안되던 변환이 가능하다. 프로그래머 에게 책임 위임한다. 컴파일 시점에 진행된다. 부모(더적은기능)객체변수에 자식(더많은기능)객체를 할당하고 또 다른 자식객체변수에 그 변수를 자식객체로 형변환하는 경우도 가능하다. (의도적으로 진행하겠다는 것을 내포한다.) (다이나믹에서 안되던 것이 가능하다.) (정말 본인이 책임가능하다면 이 형변환을 사용하자.) (속도는 static_cast가 더 빠르다.)
소수점 표현을 위해 (double)20/3 등으로 표현하던 코드를 C++에서는 static_cast<double>(20)/3 이라는 코드로 쓸 것을 권장하고 있다. static_cast는 완만히 변환해주는 것 같지만 사실 C 형변환 보다는 엄격한 기준으로 형변환을 진행한다.
- const_cast : const의 성향을 제거하라. 함수사용시 타입이 불일치 할 때 유용하게 사용될 수 있다.
bool func(int *a) {
return *a == *a;
}
컴파일러가 true로 치환할 수 있는데 컴파일러의 최적화를 막는 키워드 (volatile 성향의 제거도 가능하다.)
- reinterpret_cast : 상관없는 자료형으로의 형 변환. 전혀 상관없어보이는 형변환을 가능하게 한다. (포인터와 관련이 있는 모든 유형의 형변환을 허용한다.)
- Ploymorphic 클래스 : 하나 이상의 가상함수를 지니는 클래스이다. 해당 클래스가 하나 이상존재하는 클래스는 dynamic_cast이 가능하다.
- 내가 작성한 4개의 파일
[product.h 파일]
#pragma once
//product.h 파일에는
//product 클래스가 정의되어 있다.
//멤버로 제품명(char *prdName)와 제품금액(int prdPrice)가
//private 멤버로 구성되어있다.
#include <iostream>
#include <cstring>
#pragma warning (disable:4996)
using namespace std;
class product {
private:
char prdName[20];
int prdPrice;
public:
~product() {
cout << "product 객체 소멸" << endl;
}
void setNamePrice(char* name, int price);
void printProduct();
};
[productcontainer.h 파일]
#pragma once
// productcontainer.h
// productcontainer 클래스가 정의되어 있다.
//product형태의 더블포인터가 있다.
//더블포인터가 가질수 있는 MAX_CNT_PRODUCT 이름의 const 변수가 있다.
//또 더블포인터의 실제 제품갯수의 수인 topindex 가 있다.
//기능에는
//추가(addProduct), 수정(modProduct), 출력(printProduct)
#include "product.h"
class productcontainer {
product** products;
const int MAX_CNT_PRODUCT;
int topindex;//또 더블포인터의 실제 제품갯수의 수인 topindex 가 있다.
public:
productcontainer(int max) :MAX_CNT_PRODUCT(max) {
topindex = 0;
products = new product*[max];
for (int i = 0; i < max; ++i) {
products[i] = new product();
}
}
~productcontainer() {
cout << "productcontainer 객체 소멸" << endl;
for (int i = 0; i < MAX_CNT_PRODUCT; ++i) {
delete products[i];
}
delete[] products;
}
void addProduct(char* name, int price) {
products[topindex]->setNamePrice(name, price);
topindex++;
cout << "상품입력완료" << endl;
cout << MAX_CNT_PRODUCT - topindex << "개의 상품이 추가입력 가능합니다." << endl;
}
void modProduct(int index, char* name, int price) {
if (index >= topindex || index < 0) {
cout << "그런 상품 없습니다." << endl;
return;
}
products[index]->setNamePrice(name, price);
}
void printProduct() {
for (int i = 0; i < topindex; ++i) {
cout << "[" << i << "]번째 상품" << endl;
products[i]->printProduct();
}
}
void printProduct(int i) {
if (i >= topindex) {
cout << "그런 상품 없습니다." << endl;
return;
}
cout << "[" << i << "]번째 상품" << endl;
products[i]->printProduct();
}
};
[product.cpp 파일]
//product.cpp 파일에는
//상품이름, 상품금액을 출력하는 printProduct() 인 멤버함수가 있다.
//상품이름, 상품금액을 추가하는 setPrdInfo() 인 멤버함수가 있다.
#include "product.h"
void product::printProduct() {
// 상품: 공기청정기
// 금액 : 420000
cout << "상품 : " << prdName << endl << "금액 : " << prdPrice << endl;
}
void product::setNamePrice(char* name, int price) {
strcpy(prdName, name);
prdPrice = price;
}
[product_main.cpp 파일]
//product_main.cpp에는
#include "productcontainer.h"
void print() { //출력만을 기본적으로 하는 전역함수
cout << "***** 상품 추가 / 수정 / 출력 * ****" << endl;
cout << " 메뉴 입력 " << endl;
cout << " 1)상품추가 " << endl;
cout << " 2)상품수정 " << endl;
cout << " 3)종합출력 " << endl;
cout << " 4)상품출력 " << endl;
cout << " 5)종료 " << endl;
}
int main() {
cout << "상품 입력 개수를 입력하시오 : ";
int i;
cin >> i;
productcontainer p(i);//main함수에는 productcontainer 형 객체를 만들어 이를 관리한다.
//객체를 생성할때 해당 객체는 내부 product의 maximum 갯수를 만들수있다.
bool loop = true;
while (loop) {//선택메뉴에 따라 해당 객체 내부의 함수들로 점프한다.
print();
cin >> i;
getchar();
switch (i) {
case 5:
cout << "프로그램을 종료합니다." << endl;
loop = false;
break;
case 1:
cout << "상품명 : ";
char name[20];
cin.getline(name, 20);
cout << "상품가 : ";
int price;
cin >> price;
p.addProduct(name, price);
break;
case 3:
p.printProduct();
break;
case 4:
cout << "출력 상품 번호 : ";
cin >> i;
p.printProduct(i);
break;
case 2:
//cout << "구현 안함";
cout << "수정 상품 번호 : ";
cin >> i;
getchar();
cout << "새로운 상품명 : ";
name[20];
cin.getline(name, 20);
cout << "새로운 상품가 : ";
price;
cin >> price;
p.modProduct(i, name, price);
break;
}
}
}
'c언어 > c++' 카테고리의 다른 글
[c++] 동적으로 배열 할당하여 사용하기 (new, 포인터) (0) | 2019.07.03 |
---|---|
[STL] Template, 시퀀스 사용 등 (0) | 2019.07.02 |
[c++/cpp] 연산자 오버로딩, 복사생성자, 템플릿(T) 등 (0) | 2019.07.01 |
[c++/cpp] 생성자, 복사생성자, 소멸자 등 (0) | 2019.07.01 |
[c++/cpp] 함수 포인터, 구조체 등 (0) | 2019.07.01 |