C++(3)

ChoRong0824·2023년 3월 28일
0

C

목록 보기
10/17
post-thumbnail

7~9 : 클래스와 활용

Class and Object

분할 컴파일

  • 구조체와 함수의 원형은 헤더파일로 분류할 수 있습니다.

헤더파일에는 주로

  1. 함수 원형
  2. define이나 const를 사용하는 기호 상수
  3. 구조체 선언
  4. 클래스 선언
  5. 템플릿 선언
  6. 인라인 함수
#include <iostream>

using namespace std;

struct Mystruct{
    string name;
    int age;
};
void display(Mystruct&);

int main(){

    Mystruct Coding ={
        "Seongjun",
        27,
    }; // 이 구조체가 위의 Mystruct 의 string 이름과 나이에 대입됨

    display(Coding); // 여기서 아래 display에 코딩을 매개변수로 넣어줌
    return 0;
}

void display(Mystruct& temp){
    cout << "이름 : " << temp.name << endl;
    cout << "나이 : " << temp.age << endl;
}

헤더 파일분리, 헤더 파일 부분에 struct.h 파일 생성해주고, 아래와 같이 코드 작성해주면 됨. (전처리자에 작성함)

#include "struct.h"

int main(){

    Mystruct Coding ={
        "Seongjun",
        27,
    };

    display(Coding);
    return 0;
}

void display(Mystruct& temp){
    cout << "이름 : " << temp.name << endl;
    cout << "나이 : " << temp.age << endl;
}

소스 파일 부분에, fun.cpp 파일로 만들어주고 아래와 같이 분리해준다.

#include "struct.h"
void display(Mystruct& temp){
    cout << "이름 : " << temp.name << endl;
    cout << "나이 : " << temp.age << endl;
}

이렇게 되면 본체(몸통)엔 아래와 같은 코드가 된다.

#include "struct.h"

int main(){

    Mystruct Coding ={
        "Seongjun",
        27,
    };

    display(Coding);
    return 0;
}
  • 프로그램의 규모가 커질수록 관리하기 쉬워짐.

헤더 파일을 여러 파일에 포함시킬 때에, 반드시 단 한 번만 포함시켜야합니다.

  • 앞서 말했던 상황의 불상사를 막기 위해서, c++ 에서는
    #ifdef STRUCT 를 사용하고, 마지막 줄엔 #endif를 사용해줍니다. 이러면 STRUCT라는 이름으로 묶이게 됨. => 기호상수화해줌.

추상화와 클래스

  • 데이터형이란 ?
  • 데이터형을 대상으로 어떠한 연산을 수행할 수 있는가?
  • 클래스란 ?
    추상화를 사용자 정의 데이터형으로 변환해주는 수단
  • 추상화란 ?
    어떠한 객체를 사실적으로 표현하는 것이 아니라,
    공통된 특징을 간결한 방식으로, 이해하기 쉽게 표현하는 것입니다.
  • 클래스는 2 개로 분류할 수 있습니다.
    1. 클래스 선언
    1. 클래스 메서드 정의

예시

#include <iostream>

using namespace std;

class Stock
{
private:
    string name;
    int shares;
    float share_val;
    double total_val;
public:
    void acquire(string&, int, float);
    void buy(int, float);
    void sell(int, float);
    void update(float);
    void show();
    Stock(/* args */);
    ~Stock();
};

// 사용 범위 결정 연산자 `::`
// 생성자
Stock::Stock(/* args */)
{
}

// 파괴자
Stock::~Stock()
{
}

-> public:에 정의되어있는 멤버함수들은 private:에 정의 되어 있는 멤버 변수들의 값을 변경해주거나, 값을 변경할 수 있도록 있는 단위역할을 수행 합니다.
프로그램이 private: 에 있는 변수들의 데이터에 직접적으로 접근할 수 없고,
public: 의 멤버함수를 통해서만 접근을 할 수 있기 때문에 데이터에 대해 직접적으로는 접근하지 못합니다. -> 데이터은닉 (직접 접근 못하게함), private

즉, 정리하면

  • public
    공개 멤버, 클래스
    외부에서도 접근 가능

  • private
    비공개 멤버
    클래스 내에서만 접근 가능

집적적인 데이터는 private로 선언하여 데이터 은닉을 하자.

아래 예시와 같이 사용할경우,

void Stock::acquire(string&, int, float){

}
void Stock::buy(int,float){

}
void Stock::sell(int,float){

}
void Stock::update(float){

}
void Stock::show(){
    
}

(::)를 붙여주면, Stock 클래스 사용범위에 포함된다.

#include <iostream>

using namespace std;

class Stock
{
private:
    string name;
    int shares;
    float share_val;
    double total_val;
    // 자동으로 총 가격을 계산해주는 기능의 함수를 하나 만들기 
    void set_total(){total_val = shares * share_val;}
public:
    void acquire(string, int, float);
    void buy(int, float);
    void sell(int, float);
    void update(float);
    void show();
    Stock(/* args */);
    ~Stock();
};

void Stock::acquire(string co, int n, float pr){
    name =co;
    shares = n;
    share_val =pr;
    set_total();
}
void Stock::buy(int n,float pr){
    shares +=n;
    share_val =pr;
    set_total();
}
void Stock::sell(int n,float pr){
    shares -= n;
    share_val =pr;
    set_total();
}
void Stock::update(float pr){
    share_val =pr;
    set_total();
}
void Stock::show(){
    cout << "회사 명 : " << name <<endl;
    cout << "주식 수 : " << shares <<endl;
    cout << "주가 : " << share_val <<endl;
    cout << "주식 총 가치 : " << total_val <<endl;
}
Stock::Stock(/* args */)
{
}

Stock::~Stock()
{
}
//이제 위에 작성했던 클래스 객체를 직접 생성하고, 사용하기
int main(){
    Stock temp;
    // temp 에 관한 멤버함수를 호출해주기 위해서 멤버 연산자인 " . "을 사용해줌
    temp.acquire("Seongjun",100 ,1000);
    temp.show();
    temp.buy(10,1200);
    temp.show();
    temp.sell(5,800);
    temp.show();

    return 0;
}

생성자와 파괴자

Stock::Stock(/* args */)
{
}

Stock::~Stock()
{
cout << name << "소멸되었습니다"<<endl;
}

소멸자 = 파괴자
만약, 소멸자에 내용을 넣는다면, 파괴자는 클래스가 소멸될 때마다 호출됨.

this 포인터, 클래스 객체 배열

Stock.h

#ifndef STOCK
#define STOCK
#include <iostream>

using namespace std;

class Stock
{
private:
	string name;
	int shares;
	float share_val;
	double total_val;
	void set_total() { total_val = shares * share_val; }

public:
	Stock();
	Stock(string, int, float);
	void buy(int, float);
	void sell(int, float);
	void update(float);
	Stock& topval(Stock&);
	void show();
	~Stock();
};
#endif // !STOCK

func.cpp

#include "Stock.h"

void Stock::buy(int n, float pr) {
	shares += n;
	share_val = pr;
	set_total();
}
void Stock::sell(int n, float pr) {
	shares -= n;
	share_val = pr;
	set_total();
}
void Stock::update(float pr) {
	share_val = pr;
	set_total();
}
void Stock::show() {
	cout << "회사 명 : " << name << endl;
	cout << "주식 수 : " << shares << endl;
	cout << "주가 : " << share_val << endl;
	cout << "주식 총 가치 : " << total_val << endl;
}
Stock& Stock::topval(Stock& s) {
	if (s.share_val > share_val)
		return s;
	else return *this;
}



Stock::Stock(string co, int n, float pr)
{
	name = co;
	shares = n;
	share_val = pr;
	set_total();
}

Stock::Stock() {
	name = "";
	shares = 0;
	share_val = 0;
	set_total();
}

Stock::~Stock()
{
	
}

#include "Stock.h"

void Stock::buy(int n, float pr) {
	shares += n;
	share_val = pr;
	set_total();
}
void Stock::sell(int n, float pr) {
	shares -= n;
	share_val = pr;
	set_total();
}
void Stock::update(float pr) {
	share_val = pr;
	set_total();
}
void Stock::show() {
	cout << "회사 명 : " << name << endl;
	cout << "주식 수 : " << shares << endl;
	cout << "주가 : " << share_val << endl;
	cout << "주식 총 가치 : " << total_val << endl;
}
Stock& Stock::topval(Stock& s) {
	if (s.share_val > share_val)
		return s;
	else return *this;
}



Stock::Stock(string co, int n, float pr)
{
	name = co;
	shares = n;
	share_val = pr;
	set_total();
}

Stock::Stock() {
	name = "";
	shares = 0;
	share_val = 0;
	set_total();
}

Stock::~Stock()
{
	
}

this 포인터는 멤버함수를 호출하는데 사용된 객체를 지시합니다.
*this의 this 는 당연히 포인터라서 주소 값을 가짐

main.cpp

#include <iostream>
#include "Stock.h"

int main() {

	Stock s[4] = {
		Stock("A", 10, 1000), // 0 각 원소마다 스톡 생성자를 호출함 그생성자에 할당되어있는 값마다 각 원소로 대입함 
		Stock("B", 20, 1200), // 1
		Stock("C", 20, 1300), // 2
		Stock("D", 20, 1400) // 3
	};
	
	Stock *first = &s[0]; // Stock firs = s[0];
	for (int i = 1; i < 4; i++)
		first = &first->topval(s[i]); // first = first.topval(s[i]);

	first->show(); // firs.show(); 인데,
    // Stock Stock::topval(Stock& s){ 하고, return s; else return *this;
    //라면, 참조를 리턴하기 때문에, 탑벌에 참조 연산자(&)를 붙여주어서 바꾼다음에
    // 해당 코드에도  수정해줘야함. (직접이 아니라 간접이라서,)

	return 0;
}

Utilization of Class

연산자 오버로딩

매개변수에 데이터형이 서로 다르더라도 파라미터로 선언된 데이터형의 값에 따라서 동일한 함수 이름으로 동일한 연산을 수행하도록 하게함.

// time.h
#include <iostream>
#ifndef TIMEH
#define TIMEH

class Time
{
private:
	int hours;
	int mins;

public:
	Time();
	Time(int, int);
	void addHours(int);
	void addMins(int);
	Time operator+(Time&);
	Time operator*(int);
	void show();
	~Time();
	friend Time operator*(int n, Time& t) {
		return t * n;
	}
}

#endif //TIMEH


// func.cpp
#include "time.h"

Time::Time()
{
	hours = mins = 0;
}

Time::Time(int h, int m) {
	hours = h;
	mins = m;
}

void Time::addHours(int h) {
	hours += h;
};

void Time::addMins(int m) {
	mins += m;
	hours += mins / 60;
	mins %= 60;
};

Time Time::operator+(Time& t) {
	Time sum;
	sum.mins = mins + t.mins;
	sum.hours = hours + t.hours;
	sum.hours += sum.mins / 60;
	sum.mins %= 60;
	return sum;
};

void Time::show() {
	std::cout << "시간 : " << hours << std::endl;
	std::cout << "분 : " << mins << std::endl;
}

Time::~Time()
{
}

Time Time::operator*(int n) {
	Time result;
	long resultMin = hours * n * 60 + mins * n;
	result.hours = resultMin / 60;
	result.mins = resultMin % 60;
	return result;
}

Class Inheritance

c++ 멤버변수 private으로 데이터 은닉시 접근하는 방법

C++에서 멤버 변수를 private으로 설정하면, 해당 변수는 클래스 외부에서 직접 접근이 불가능해집니다. 이때, 멤버 변수에 접근하려면 다음과 같은 방법을 사용할 수 있습니다.

Getter 함수를 사용하는 방법
Getter 함수는 멤버 변수의 값을 반환하는 함수입니다. 이 함수를 클래스 내부에 선언하고, public으로 설정하여 클래스 외부에서도 접근이 가능하도록 합니다. 다음은 Getter 함수를 사용하는 예시입니다.

class MyClass {
private:
    int myVar;

public:
    int getMyVar() const {
        return myVar;
    }
};

위 코드에서 getMyVar() 함수는 myVar 멤버 변수의 값을 반환합니다. 이 함수는 클래스 외부에서도 접근이 가능하며, 다음과 같이 사용할 수 있습니다.

MyClass obj;
int var = obj.getMyVar();

Setter 함수를 사용하는 방법
Setter 함수는 멤버 변수의 값을 설정하는 함수입니다. 이 함수를 클래스 내부에 선언하고, public으로 설정하여 클래스 외부에서도 값을 변경할 수 있도록 합니다. 다음은 Setter 함수를 사용하는 예시입니다.

class MyClass {
private:
    int myVar;

public:
    void setMyVar(int value) {
        myVar = value;
    }
};

위 코드에서 setMyVar() 함수는 myVar 멤버 변수의 값을 설정합니다. 이 함수는 클래스 외부에서도 접근이 가능하며, 다음과 같이 사용할 수 있습니다.

MyClass obj;
obj.setMyVar(10);

위 예시에서 setMyVar() 함수를 호출하여 myVar 멤버 변수의 값을 10으로 변경합니다.

위와 같이 Getter 함수와 Setter 함수를 사용하여 private으로 선언된 멤버 변수에 접근할 수 있습니다. 하지만, 이 방법은 일반적으로 노출되지 않아야 할 데이터를 보호하는 데에 큰 도움을 줍니다.

명품 c++ 실습 2-16번

#include <iostream>
#include <cstring>
using namespace std;

int main() {

	int countStack = 0, numCount = 0;
	char buf[10000];

	cout << "영문 텍스트를 입력하세요. 히스토그램을 그립니다." << endl;
	cout << "텍스트의 끝은 ; 입니다. 10000개까지 가능합니다." << endl;

	cin.getline(buf, 10000, ';');

	for (int i = 0; i <= strlen(buf); i++)
	{
		if (isalpha(buf[i]) != 0)
		{
			buf[i] = tolower(buf[i]);
			numCount++;
		}
	}

	cout << "총 알파벳 수 " << numCount << endl;

	for (char i = 'a'; i <= 'z'; i++)
	{
		for (int j = 0; j <= strlen(buf); j++)
			if (buf[j] == i)
				countStack++;
		cout << i << "(" << countStack << ")" << " : ";
		for (int k = 0; k < countStack; k++)
			cout << "*";
		cout << endl;
		countStack = 0;
	}
}

isalpha(char c) 함수가 알파벳인지 검사하기 위해서 사용하라는 힌트를 보고 구분하는 것을 봤습니다.

숫자 등의 알파벳을 제외하고 찾으려면 ==0, 알파벳을 비교해서 찾고 싶으면 !=0을 작성해 줘야 합니다.

tolower(char c) 함수는 대문자를 소문자로 바꾸는 함수입니다.

isalpha로 알파벳이라면 tolower를 사용하여 buf에 저장된 값이 문자 값을 소문자로 i 번 째 만큼 값을 변경해둡니다.

이후 검색하며 출력하면 됩니다.

streln() 함수의 경우 문자열 길이를 반환하는 함수입니다. 그 문자열의 길이만큼 for 문을 돌려준다는 의미로 사용

profile
정진, "어제보다 더 나은 오늘이 되자"

0개의 댓글