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개의 댓글