728x90

0) 안드로이드의 삼대장&원수 (??)

출처: https://www.youtube.com/watch?v=49N59HnCAxQ&t=109s 

- 원수: Activity !!! : foreground(눈에 보일 때만) 일을 하는 녀석
   액티비티는 곧 화면 (UI를 보여주고 실제 기능까지 한다. 다 한다.)

- 대장 1: ContentProvider: 자료나 데이터 받아오는 경우 (사용하지 않는 경우 있음)

- 대장 2: Service: background(눈에 보이지 않는데) 일을 하는 녀석

- 대장 3: BroadcastReceiver: 알림을 도와주는 녀석 (ex. 카톡 메시지가 왔을 때 '메시지 왔다'는 사실을 알려주는 것.) 콜백과 비슷한 기능.. 통신 느낌.

https://www.youtube.com/watch?v=49N59HnCAxQ&t=109s

1) Activity의 활동 수명

https://developer.android.com/guide/components/activities/activity-lifecycle?hl=ko


2) View: 화면 그 자체

  • Activity에 씌우는 화면 껍데기
  • XML, Java 둘 중 하나로 작성 가능 (보통 XML)
    • xml파일에 <TextView.../> 하는 게 다 여기에 해당
  • 보통 widget, adapter, layout 계열로 나눌 수 있음
    • widget; TextView, ImageView 등. 용도가 뚜렷함.
    • adapter; ListView, GirdView, RecyclerView 등. 여러 개의 많은 정보를 길게 스크롤하여 나열할 때 많이 씀

3) Layout

  • layout; LinerLayout, RelativeLayout, FrameLayout... 화면 공간 배분할 때 많이 쓰임.

4-1) Thread

- 작업의 흐름

- 기본적으로 main / UI thread가 존재함

4-2) Multi threading : 분업

출처: https://www.youtube.com/watch?v=7oeSMTfZ2mo&t=1129s 

https://www.youtube.com/watch?v=7oeSMTfZ2mo&t=1129s

- 단!! 무조건 많을수록 좋은 것은 아니다: 빠른 속도로 다양한 일을 하기에, 동시에 되는 것처럼 착각하는 경향이 있음.

- 일반적인 앱의 경우, main에 1~2개 Thread만 멀티로 두는 편

https://www.youtube.com/watch?v=7oeSMTfZ2mo&t=1129s


https://www.youtube.com/watch?v=7oeSMTfZ2mo&t=1129s

- Thread: java에서 제공하는 클래스

- Handler: android에서 제공하는 클래스

 

방법 1: 자바스러움

1) Thread가 실제 계산/어떤 일을 수행하고 나서. 일을 다 한 내역을 Handler thread에 보내줌

2) Thread로부터 받아온 일 결과물을, Handler를 통해 간접적으로 반영해줌

 

방법 2: AsyncTask

- Android Recommended

- 간단하게 새로운 쓰레드를 생성해서 작업할 수 있음 (코드도 간결)

 

 


https://www.youtube.com/watch?v=LvBNVuXbS3E 

 

728x90
728x90

인터페이스의 쓰임새

*UI(유저 인터페이스) 아님!!!

  • 다중 상속
  • 콜백 메소드

인터페이스의 기능: 1) 다중 상속

1. Java에서는 2개 이상의 상속이 불가능하다

2. '상속(extends)' 대신 '구현(implements)'하자.

3. by '인터페이스'

 

상속 (extends) 구현 (implements)
- 아이덴티티를 보유한다
ex) 개는 동물이다 (개는 동물에 완전히 속하므로)
- 역할(role)을 부여한다
ex) 개는 반려동물이다 (일부 개에게만 해당)

- 구현 implements은 변수를 상속받지 않는다 (오로지 행동 메서드만 취함)

 

 attackable이라는 interface를 만들면 우리가 지정한 대상에 한해서 attack이라는 메소드를 공유할 수 있도록 함

(부모클래스에 attack 포함하면 그것을 상속하는 모든 클래스가 attack할 수 있게 되므로 No)

 

https://www.youtube.com/watch?v=49COMLtdY04

→ sell 파는 것 & upgrade : Trade와 관련된 것들은 서로 공유할 수 있도록 함.

 


인터페이스의 기능: 2) 콜백 함수

Callback

사용자가 버튼을 눌렀을 때(event) 뭔가 하고싶어

- 앱을 사용하다가 홈버튼을 누르면 어떻게 처리하지?

- 다운로드가 완료되었으면 알림을 울릴 수 없을까?

 

콜백 메소드: 이러한 시점(event)에 호출되는 메소드

Q. 어떤 게 콜백이지?

- 콜백 메소드는 보통 onClick, onTouch, onPressed 등 on~이 붙는다.

- 콜백을 정의한 인터페이스는 보통 OnClickListener, OnTouchListener 등 Listener가 붙는다.

https://www.youtube.com/watch?v=IBxiy7dzv_4&t=333s

 

- OnDownloadListener: 인터페이스

- Download(this)의 this는 Downloader 클래스일 수도 있고, OnDownloadListener 인터페이스일 수도 있는데, 실제 Download 함수 정의한 내용에서 매개변수로 오직 OnDownloadListener만을 취하므로, 후자를 가리키게 됨

- Download 함수에서 정의한 코드가 완료되면 listener 인터페이스에 onDownloadFinish()라는 메소드가 실행됨.

- "다운로드라는 이벤트를 완료했어"라는 알림을 onDownloadFinish라는 콜백함수가 listener 인터페이스에게 전달해주는 것임.

- onDownloadFinish 콜백 함수는, 실제로 OnDownloadListener이라는 인터페이스로부터 받아온 (@Override) 함수임.

 



reference: 겜팔이의 안드로이드 세뇌교실

728x90

728x90

이벤트 내용

 

- Count 버튼을 누르면 수를 카운트 하고

- 카운트 한 만큼 화면에 띄우기

- Reset 버튼을 누르면 카운트 값 리셋하기


MainActivity.java

package com.example.cute_tutorial;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    private int count = 0;
    private TextView countText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        countText = (TextView) findViewById(R.id.count_text);
        countText.setText("0");
    }

    public void buttonCount(View view) {
        count++;
        if (countText != null) {
            countText.setText(Integer.toString(count));
        }
    }

    public void buttonReset(View view) {
        count = 0;
        if (countText != null) {
            countText.setText(Integer.toString(count));
        }
    }
}

 

private int count = 0;
private TextView countText;

 

Android 개발에서 TextView는 사용자 인터페이스(UI)를 구성하는 데 사용되는 위젯(Widget) 중 하나

- 주요 기능: 텍스트를 화면에 표시 (정적/동적)

 

- 레이아웃 파일(XML) 또는 Java 코드에서 사용됨

(1) XML에서 TextView 위젯 추가

<TextView
    android:id="@+id/myTextView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello, World!"
    android:textSize="18sp"
    android:textColor="#000000"
    android:gravity="center"
    android:layout_gravity="center"/>

 

(2) Java 코드에서 TextView를 조작하려면, findViewById() 메서드를 사용하여 xml에 정의된 TextView의 id값을 참조하는 방식으로 해당 TextView 객체를 가져오면 됨

- 객체를 가져오기만 하면, 나머지 이벤트는 기본 프로그래밍 방식으로 처리.

 

countText = (TextView) findViewById(R.id.count_text);
countText.setText("0");

 

- 안드로이드 앱 내 xml 파일에서 'count_text'라는 Id를 가진 TextView를 찾고

- 해당 TextView에 "0"이라는 텍스트를 보이게 하는 코드

 

findViewById(R.id.count_text)에서의 R은 안드로이드 앱의 리소스(Resources)를 나타내는 클래스

R 클래스는 앱이 컴파일될 때 자동으로 생성되며, 리소스의 유형과 이름에 따라 여러 하위 클래스를 가짐

- R.layout는 레이아웃 리소스에 대한 상수를 포함하는 클래스

- R.id 레이아웃 내의 뷰에 할당된 고유한 ID 관리

 

    public void buttonCount(View view) {
        count++;
        if (countText != null) {
            countText.setText(Integer.toString(count));
        }
    }

 

아까 id 로 객체 지정한 countText를 바꾸어주는 함수 설정

해당 함수는 xml 파일에서 정의된 위젯들 중에, buttonCount라는 이벤트와 연결되어 있는 버튼이 눌렸을 때 동작함

 


activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:layout_marginStart="137dp"
        android:layout_marginTop="246dp"
        android:text="Hello World!" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:layout_marginStart="62dp"
        android:layout_marginTop="66dp"
        android:text="Name" />

    <Button
        android:id="@+id/buttonCount"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginStart="158dp"
        android:layout_marginTop="316dp"
        android:text="Count!"

        android:onClick="buttonCount"/>

    <Button
        android:id="@+id/buttonReset"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="291dp"
        android:text="Reset!"

        android:onClick="buttonReset"
        />

    <TextView
        android:id="@+id/count_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="210dp"
        android:text="TextView" />

</RelativeLayout>

 

728x90
728x90

1. 동적결합(Dynamic Binding)의 개념

프로그램이 실행 중일 , 어떤 함수나 메서드를 호출할 실제로 어떤 코드를 실행할지를 결정하는 것

- 즉, 함수 호출이 어떤 객체나 클래스의 실제 버전을 호출할지 런타임(runtime) 시점에 결정하는 

- 주로 객체 지향 프로그래밍에서 다형성을 구현하는 사용됨

 

1) '프로그램 실행 중'에 객체의 실제 타입을 고려

2) 해당 타입에 맞는 함수나 메서드를 호출

 

- 이를 통해 서로 다른 객체 동일한 함수 또는 메서드 호출하더라도, 객체의 고유한 동작 수행할 있음


2) c++에서 동적 결합을 구현하는 두 가지 주요 메커니즘

 

2-1. 가상함수 (virtual functions)

- 함수 앞에 virtual 키워드를 사용

- 가상 함수는 기본형 클래스에서 선언되고 파생 클래스(derived class)에서 재정의(override)할 수 있는 함수

- 가상 함수를 사용하면 런타임 시에 객체의 실제 타입에 따라 올바른 함수 버전이 호출됨

class Shape {
public:
    virtual void draw() {
        // 기본 구현 }
};

class Circle : public Shape {
public:
    void draw() override {}
};

2-2. 가상테이블 (virtual table)

- 가상 함수를 사용할 때, C++ 컴파일러는 객체에 대한 가상 함수 포인터 테이블을 생성함

- 테이블은 가상 함수의 주소를 저장하고, 실제 함수 호출 시에 테이블을 참조하여 올바른 함수를 호출함

 

void drawShape(Shape* shape) {
    shape->draw(); // 동적 결합
}

int main() {
    Circle circle;
    Square square;

    drawShape(&circle); // Circle 클래스의 draw()가 호출됨
    drawShape(&square); // Square 클래스의 draw()가 호출됨

    return 0;
}

구체적인 예제로 동적 결합 이해하기

 

기초형 클래스의 pointer으로 구성된 포인터 array를 선언

→ 해당 array에 기초형 클래스 Time뿐 아니라, 상속 클래스인 NewTime의 주소값도 저장됨

array의 각 원소에 대해 virtual 메서드인 show()를 실행시켜줌

(show 실행될 때마다, 어떤 class 타입인지 실시간으로 판단해스 해당 타입에 대응되는 virtual 메서드를 실행시킴)

 

time.h

#include<iostream>
using namespace std;

#ifndef FIRE_C_TIME_H
#define FIRE_C_TIME_H

class Time{
private:
    int hours;
    int mins;
public:
    Time();
    Time(int, int);

    /* virtual 통해 가상 메소드 선언 */
    virtual void show(); //가상메소드
    virtual ~Time(); //파괴자 역시 가상 메소드로 선언

    int getHour(){return hours;}
    int getMins(){return mins;}

    Time operator*(int n);
    friend Time operator*(int, Time&);
};

class NewTime : public Time {
private:
    int day;
public:
    NewTime(); //생성자
    NewTime(int, int, int); //생성
    void show();
};

#endif //FIRE_C_TIME_H

time_func.cpp

#include "time.h"

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

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

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

Time::~Time(){}

NewTime::NewTime() : Time(){
day = 0;
}

NewTime::NewTime(int h, int m, int d) : Time(h, m) {
day = d;
}

void NewTime::show(){
    cout << day << "일 " << getHour() << "시간 " << getMins() << "분" << endl;
}

main.cpp

#include "time.h"
#include "time_func.cpp"

const int MAX = 3;

int main(){
    Time* times[MAX]; //기초형 Time 클래스의 포인터들이 저장된 배열
    //이곳에 Time 기초형 클래스의 포인터도, NewTime 상속 클래스의 포인터도 담김
    int day;
    int hours;
    int mins;

    for (int i = 0; i < MAX; i++) {
        cout << i + 1 << "번째 원소를 정의합니다." << endl;
        cout << "시간을 입력하십시오." << endl;
        cin >> hours;
        cout << "분을 입력하십시오." << endl;
        cin >> mins;
        char check;
        cout << "일 정보가 있다면 1, 없다면 0을 입력하십시오." << endl;
        cin >> check;
        if (check == '0')
            times[i] = new Time(hours, mins);
        else {
            cout << "일을 입력하십시오." << endl;
            cin >> day;
            times[i] = new NewTime(hours, mins, day);
        }
    }

    for (int i = 0; i < MAX; i++){
        cout << i+1 << "번째 정보입니다." <<endl;
        times[i]->show();
        //show를 virtual로 선언했기 때문에,
        //times: time의 pointer 배열이지만
        //Times이면 Time의 show()가, NewTime이면 NewTime의 show()가 실행됨
    }

    for (int i = 0; i < MAX; i++){
        delete times[i]; //delete의 경우 time형에 대한 delete만 이루어짐
        //NewTime 객체의 경우 제대로 delete되지 않음
        //이를 위해 파괴자 역시 virtual로 선언해야 함

    }

    return 0;}
728x90
728x90

1. 기초 클래스에서 가상 메서드를 선언하면, 그 함수는 기초 클래스 및 파생되는 클래스에서 모두 가상이 됨

2. 객체에 대한 참조 or 포인터를 사용하여, 가상 메서드가 호출되면 → 참조나 포인터를 위해 정의된 메서드를 사용하지 않고 객체형을 위해 정의된 메서드를 사용한다. (=동적 결합)

3. 상속을 위해 기초 클래스로 사용할 클래스를 정의할 때, 파생 클래스에서 다시 정의해야 되는 클래스 메서드들은 가상 함수로 선언해야 한다.

 


기초 클래스의 데이터와 상속 클래스의 데이터를 출력하는 함수(메서드)를 만들고자 한다.

기존에는 각각에 서로 다른 메서드 show() / print() 를 정의했었다면,

이번에는 동일한 이름의 show() 메서드로 기초 클래스와 상속 클래스의 데이터를 출력해주고자 한다.

이를 위해서는 기초 클래스의 public 영역에서, virtual 키워드를 통해 show 메서드를 '가상 메서드'로 선언하여야 한다.

 

time.h

#include<iostream>
using namespace std;

#ifndef FIRE_C_TIME_H
#define FIRE_C_TIME_H

class Time{
private:
    int hours;
    int mins;
public:
    Time();
    Time(int, int);

    /* virtual 통해 가상 메소드 선언 */
    virtual void show();
    //가상 메소드: 프로그램에서 서로 독립된 메소드 정의가 있다는 것을 알려줌
    //show가 호출되었을 때, 호출한 객체를 따져서, 대응되는 함수를 선택함
    //현재 show가 가상 메소드로 정의된 상태

    int getHour(){return hours;}
    int getMins(){return mins;}
    ~Time();

    Time operator*(int n);
    friend Time operator*(int, Time&);
};

class NewTime : public Time {
private:
    int day;
public:
    NewTime(); //생성자
    NewTime(int, int, int); //생성
    void show();
};

#endif //FIRE_C_TIME_H

 

time_func.cpp

#include "time.h"

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

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

// (1) 기초형 클래스에서 정의된 show() 메서드
void Time::show(){
    cout << hours << "시간 " << mins << "분" << endl;}

Time::~Time(){}

NewTime::NewTime() : Time(){
day = 0;
}

NewTime::NewTime(int h, int m, int d) : Time(h, m) {
day = d;
}

// (2) 상속 클래스에서 정의된 show() 메서드
/* show가 어디에서 호출되느냐에 따라 다른 기능 수행 - Time vs NewTime */

void NewTime::show(){
    //show(); 이름이 동일해서 재귀적으로 호출되는 문제 발생
    //기초형 클래스의 public 함수를 새로 생성 - private 멤버변수를 반환해주는.
    cout << day << "일 " << getHour() << "시간 " << getMins() << "분" << endl;
}

 

main.cpp

#include "time.h"
#include "time_func.cpp"

//public 함수를 다형으로 사용하기

int main(){
    Time temp1(1, 2);
    NewTime temp2(3, 30, 2);

    temp1.show();
    temp2.show();

    return 0;}

/*1시간 2분
2일 3시간 30분*/

 

728x90

'개발 > c++' 카테고리의 다른 글

[c++] 서로 다른 & (참조자/주소반환)  (0) 2023.09.24
[c++] 동적 결합  (0) 2023.09.11
[c++] 클래스 상속  (0) 2023.09.10
[c++] friend (feat. 연산자 오버로딩 응용)  (0) 2023.09.10
[c++] 연산자 오버로딩  (0) 2023.09.10
728x90

- 기초 클래스 (오리지널 원본)

 

- 파생 클래스

1. 파생 클래스형의 객체 안에는 기초 클래스형의 데이터 멤버들이 저장됨

2. 파생 클래스형의 객체는 기초 클래스형의 메서드를 사용할 수 있음

3. 단, 기초 클래스형의 private member에는 접근할 수 없음 → 멤버 초기자 리스트를 통해 접근할 수 있게 됨

4. 파생 클래스는 자기 자신의 생성자를 필요로함

5. 파생 클래스는 부가적인 데이터 멤버들과 멤버 함수를 임의로 추가할 수 있음

 

**아래 주석코드의 설명 잘 읽기,,

 

time.h

#include<iostream>
using namespace std;

#ifndef FIRE_C_TIME_H
#define FIRE_C_TIME_H

class Time{
private:
    int hours;
    int mins;
public:
    Time();
    Time(int, int);
    void show();
    ~Time();
};

/*클래스 상속*/
//파생 클래스: 오리지널 클래스인 Time을 상속받음
class NewTime : public Time {
private:
    int day;
public:
    NewTime(); //생성자
    NewTime(int, int, int); //생성자
    void print();

};

#endif //FIRE_C_TIME_H

time_func.cpp

#include "time.h"

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

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

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

Time::~Time(){}


// " : Time()" 부분은 '멤버 초기자 리스트'
// 기초형 클래스의 private member에 접근할 수 있게 됨
NewTime::NewTime() : Time(){
//Time()이 먼저 생성된 뒤 해당 내용이 NewTime에 전달되어, hours = mins = 0으로 초기화됨
//NewTime()에서는 day만 초기화해주면 됨
day = 0;
}

NewTime::NewTime(int h, int m, int d) : Time(h, m) {
//Time(h,m)이 생성된 뒤 NewTime의 h, m에 전달해줌
//d만 따로 정의해주면 됨
day = d;
}

void NewTime::print(){
    cout << day << "일 " << endl;
    show();
    // hours와 mins는 NewTime의 멤버변수가 아니므로 직접 접근하지는 못함
    // 따라서 기초형 클래스의 함수 통해 출력함
}

main.cpp

#include "time.h"
#include "time_func.cpp"

int main(){
    NewTime temp1();
    NewTime temp2(3, 30, 2);

    temp2.print();

    return 0;}

/*2일
3시간 30분*/
728x90
728x90

1. friend 개념

- 클래스의 멤버 함수는 아니지만, 해당 클래스의 private 멤버에 접근할 수 있도록 하는 도구

- 형태: friend 함수 / friend 클래스 / friend 연산자 오버로딩

** friend 함수는 멤버함수가 아니므로 .이나 -> 통해 호출 불가능

** 그러나 private 멤버변수에 대해, 멤버함수와 동등한 접근 권한을 가짐

 

* 주의

- friend는 비공개 멤버에 대한 접근을 허용하는 기능이므로 남용하지 않는 것이 안전

- 클래스 간의 관계를 명확하게 구성하고, 필요한 경우에만 friend 사용할

 

2. friend를 사용하는 주요 경우

2-1. friend 함수: '함수'를 클래스의 freind로 선언

해당 함수는 클래스의 멤버 함수가 아니지만 클래스의 private 멤버에 접근할 수 있게 됨

class MyClass {
private:
    int privateData;

public:
    MyClass(int data) : privateData(data) {}
    friend void friendFunction(MyClass obj); // 'friend 함수' 선언};

→ friendFunction 함수는 MyClass 클래스 '외부'에서 정의되어 있음

외부에서 정의된 함수가 MyClass의 멤버 변수에 접근하려면, MyClass 클래스 내부에 그것의 원형이 선언되어 있어야 함

→ 더 원하는 친구가 직접 발품 팔기.~! ㅎㅎ

 

 

2-2. friend 클래스: '클래스'를 다른 클래스의 friend로 선언

다른 클래스의 private 멤버에 접근할 수 있게 됨

class FriendClass {
private:
    int secretData;
public:
    FriendClass(int data) : secretData(data) {}};


class MyClass {
private:
    int privateData;
public:
    MyClass(int data) : privateData(data) {}
    friend class FriendClass; // FriendClass는 MyClass의 private member에 접근할 수 있음};

→ 마찬가지로, FriendClass 역시 MyClass '외부'에 정의되어 있음 

 

2-3. friend 함수와 friend 클래스를 모두 사용하는 경우

#include <iostream>

class FriendClass;

class MyClass {
private:
    int privateData;

public:
    MyClass(int data) : privateData(data) {}

    // FriendClass를 friend 클래스로 선언
    friend class FriendClass;

    void PrintPrivateData() {
        std::cout << "MyClass privateData: " << privateData << std::endl;
    }
};

class FriendClass {
private:
    int secretData;

public:
    FriendClass(int data) : secretData(data) {}

    void AccessMyClassPrivateData(MyClass& obj) {
        std::cout << "FriendClass accessing MyClass privateData: " << obj.privateData << std::endl;
    }
};

int main() {
    MyClass myObj(42);
    FriendClass friendObj(100);

    myObj.PrintPrivateData(); // MyClass의 멤버 함수를 통한 privateData 접근
    friendObj.AccessMyClassPrivateData(myObj); // FriendClass를 통한 privateData 접근

    return 0;
}

연산자 오버로딩에서 friend가 사용되는 사례

- 어떤 클래스에 이항 연산자를 오버로딩할 경우 friend 필요한 경우 발생함 (*이항 연산자 : 개의 피연산자를 요구하는 연산자)

 

(1) friend만을 이용해서 연산자 오버로딩 하는 경우

(단, 입력 매개변수가 'int * 클래스' 순서를 지켜야 함)

 

time.h

#include<iostream>
using namespace std;

#ifndef FIRE_C_TIME_H
#define FIRE_C_TIME_H

class Time{
private:
    int hours;
    int mins;
public:
    Time();
    Time(int, int);
    void show();
    ~Time();
    
    friend Time operator*(int n, Time& t); //friend를 활용한 연산자 오버로딩
};

#endif //FIRE_C_TIME_H

time_func.cpp

#include "time.h"

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

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

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

Time::~Time(){}

// friend 통해 선언한 함수를 정의할 때
// 그것은 멤버함수가 아니므로 클래스 귀속 :: 표시도 하지 않고, friend를 명시하지도 않음
// 매개변수 입력 순서 중요
Time operator*(int n, Time& t){ //n*t로 해석됨 
    Time result;
    long resultMin = t.hours * n * 60 + t.mins * n;
    result.hours = resultMin / 60;
    result.mins = resultMin % 60;
    return result;
}

main.cpp

#include "time.h"
#include "time_func.cpp"

//c++ friend: public 이외에도 private 멤버변수에 접근할 수 있는 또 다른 방법
int main(){

    Time t1(1, 20);
    Time t2;
    Time t3;

    t2 = 3 * t1; //순서 바뀌면 안 됨
    t2.show();
    
    return 0;
}

(2) 사실.. 굳이 friend 쓰지 않아도 구현됨 (단, 입력 매개변수가 '클래스 * int' 순서를 지켜야 함)

 

time.h

#include<iostream>
using namespace std;

#ifndef FIRE_C_TIME_H
#define FIRE_C_TIME_H

class Time{
private:
    int hours;
    int mins;
public:
    Time();
    Time(int, int);
    void show();
    ~Time();

    Time operator*(int n);
};

#endif //FIRE_C_TIME_H

time_func.cpp

#include "time.h"

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

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

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

Time::~Time(){}

// friend를 사용하지 않고 클래스의 멤버함수로 정의할 때
Time Time::operator*(int n){
    Time result;
    long resultMin = hours * n * 60 + mins * n;
    result.hours = resultMin / 60;
    result.mins = resultMin % 60;
    return result;
}

main.cpp

#include "time.h"
#include "time_func.cpp"

int main(){

    Time t1(1, 20);
    Time t2;

    t2 = t1 * 4;
    t2.show();

    return 0;
}

//5시간 20분

(3) 두 개의 입력 매개변수 순서를 자유롭게 하고 싶을 때 ~!

→ 그러면 friend 이용하는 경우와 이용하지 않는 경우 모두 활용하면 됨 (단순..)

 

time.h

#include<iostream>
using namespace std;

#ifndef FIRE_C_TIME_H
#define FIRE_C_TIME_H

class Time{
private:
    int hours;
    int mins;
public:
    Time();
    Time(int, int);
    void show();
    ~Time();

    Time operator*(int n); //멤버함수
    friend Time operator*(int, Time&); //friend 함수
};

#endif //FIRE_C_TIME_H

time_func.cpp

#include "time.h"

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

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

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

Time::~Time(){}


// friend를 사용하지 않고 클래스의 멤버함수로 정의할 때
Time Time::operator*(int n){
    Time result;
    long resultMin = hours * n * 60 + mins * n;
    result.hours = resultMin / 60;
    result.mins = resultMin % 60;
    return result;
}

// friend 통해 선언한 함수를 정의할 때
// 그것은 멤버함수가 아니므로 클래스 귀속 :: 표시도 하지 않고, friend를 명시하지도 않음
Time operator*(int n, Time& t){
    return t*n;
}

main.cpp

#include "time.h"
#include "time_func.cpp"

int main(){

    Time t1(1, 20);
    Time t2;

    t2 = t1 * 4;
    t2.show();

    t2 = 4 * t1;
    t2.show();

    return 0;
}

/*5시간 20분
5시간 20분*/

 

728x90
728x90

1. 핵심 요약

1-1. 정의

연산자 오버로딩이란?

사용자 정의 데이터 타입(클래스)에 대한 연산자 동작을 redefine & expand

 C++ 객체 지향 프로그래밍의 핵심 원칙 하나인 다형성(polymorphism) 구현

 

- 다양한 연산자(예: +, -, *, /, ==, != 등)에 대해 사용됨

- 기존 연산자는 주로 특정 데이터타입에 국한되어 사용됨. 그러나 연산자 오버로딩을 통해, 사용자 정의 클래스의 객체를 일반적인 데이터 타입처럼 사용할 수 있음

 

*오류 방지하려면, 연산자의 의미와 클래스에 대한 연산 규칙을 명확히 이해해야

 

1-2. 연산자 오버로딩의 형식

- 연산자 오버로딩은 연산자(operator)를 정의한 클래스 내에서 진행됨

 

반환_타입 operator 연산자_이름(매개변수_목록) {

    // 연산을 정의한 코드

}

 

1-3. 연산자 오버로딩의 (일반화된?) 예시

- 클래스 내부에 연산자 오버로딩 정의

class Complex {
private:
    double real;
    double imag;

public:
    Complex(double r, double i) : real(r), imag(i) {}

    Complex operator+(const Complex& other) {
        return Complex(real + other.real, imag + other.imag);
    }
};

// Complex 클래스는 + 연산자를 오버로딩하여 두 개의 복소수를 더할 수 있도록 함.

- 연산자 오버로딩의 사용

Complex a(2.0, 3.0);
Complex b(1.0, 2.0);
Complex c = a + b; // + 연산자가 오버로딩되어 두 복소수를 더함

// a + b는 연산자 오버로딩된 + 연산자에 의해 두 Complex 객체가 더해짐

2. 구체적인 예시

2-1. 예시 요약

헤더파일에서

Time operator+(Time&);

함수 정의 파일에서

Time Time::operator+(Time& time){
    Time temp;
    temp.mins = mins + time.mins;
    temp.hours += temp.mins / 60;
    temp.mins %= 60;

    return temp;
}

main 파일에서

    Time total;
    total = day1.operator+(day2);

2-2. 예시 전문

 

1. time.h

#include<iostream>

#ifndef FIRE_C_TIME_H
#define FIRE_C_TIME_H

using namespace std;

class Time{
private:
    int hours;
    int mins;
public:
    Time(); //클래스 생성자 //함수 오버로딩
    Time(int, int); //함수 오버로딩
    void addHours(int);
    void addMins(int);
//    Time sum(Time&);
    Time operator+(Time&);
    void show();
    ~Time(); //클래스 파괴자
};

#endif //FIRE_C_TIME_H

2. time_func.cpp

#include "time.h"

Time::Time(){
    hours = 0;
    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 = mins % 60;
}

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

Time Time::operator+(Time& time){
    Time temp;
    temp.mins = mins + time.mins;
    temp.hours += temp.mins / 60;
    temp.mins %= 60;

    return temp;
}

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

Time::~Time(){

}

3. main.cpp

#include "time.h"
#include "time_func.cpp"

int main() {
    Time day1(1, 40);
    Time day2(2, 30);

    day1.show();
    day2.show();

    //오버로딩한 연산자 사용 방법 (1)
    Time total;
    total = day1.operator+(day2);
    //기존에 만들었던 sum 함수 역할을 수행하는 연산자 오버로딩 하려면
    //함수 있던 자리에 'operator' , '오버로딩 할 연산자'의 연속으로 표현하면 됨

    //오버로딩한 연산자 사용 방법 (2)
    Time total2;
    total2 = day1 + day2; //혹은 아예 연산자만 사용해도 됨

    total.show();
    total2.show();

    return 0;
}

/*1시간 40분
2시간 30분
1시간 10분
1시간 10분*/

 

728x90
728x90

1) this 포인터

: 멤버함수를 호출하는 데 사용된 객체를 지시 (본질은 포인터 ; 주소값을 리턴)

 

#ifndef FIRE_C_STOCK_STRUCTURE_H
#define FIRE_C_STOCK_STRUCTURE_H
#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 show();
    Stock compare(Stock& stock);

    Stock(string, int, float); 
    Stock(); 
};

void Stock::show() {
    cout << "회사 명 : " << name << endl;
    cout << "주식 수 : " << shares << endl;
    cout << "주가 : " << share_val << endl;
    cout << "주식 총 가치 : " << total_val << endl << endl;
}

Stock Stock::compare(Stock& stock){
    if(stock.total_val >= total_val)
        return stock;
    else return *this; //this pointer //호출한 객체 자기 자신을 가리킴
}

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

Stock::Stock(){
    name = "None";
    shares = 0;
    share_val = 0;
    set_total();
}
#endif //FIRE_C_STOCK_STRUCTURE_H
#include "stock_structure.h"

int main() {

    Stock temp = Stock("Panda", 100, 1000);
    Stock temp2("another", 200, 2000);
    temp.compare(temp2).show();
    return 0;}

/* 주식 총 가지가 더 높은 stock 클래스 객체가 출력됨
 * 
회사 명 : another
주식 수 : 200
주가 : 2000
주식 총 가치 : 400000*/

 

2) 클래스로 이루어진 배열

#include "stock_structure.h"

int main() {

    Stock s[4] = { //배열의 각 원소는 ;가 아닌 , 로 구분
            Stock("A", 10, 1000),
            Stock("B", 20, 2000),
            Stock("C", 30, 3000),
            Stock("D", 40, 4000)
    };

    Stock first = s[0]; //first: stock 배열에서 total이 가장 높은 것을 저장
    for(int i=1; i<4; i++)
        first = first.compare(s[i]);

    first.show();
    return 0;
}

→ 기존에 배열 만들고 활용하는 방식과 완전 동일함

 

**참조자 사용

#include "stock_structure.h"

int main() {

    Stock s[4] = { //배열의 각 원소는 ;가 아닌 , 로 구분
            Stock("A", 10, 1000),
            Stock("B", 20, 2000),
            Stock("C", 30, 3000),
            Stock("D", 40, 4000)
    };

    Stock* first = &s[0]; //값을 직접 대입하는 것이 아니라 주소로 참조하도록 함
    for(int i=1; i<4; i++)
        first = &first->compare(s[i]); //왜지왜지왜지왜지

    first->show();
    return 0;
}
#ifndef FIRE_C_STOCK_STRUCTURE_H
#define FIRE_C_STOCK_STRUCTURE_H
#include <iostream>
using namespace std;

class Stock {
private:
    //private member의 값을 변경하려면 public에 선언된 함수를 통해서만이 가능
    //private member에는 '직접' 접근할 수 없음 : 데이터 은닉
    string name;
    int shares;
    float share_val;
    double total_val; //double: float과 같이 실수를 표현하되 더 넓은 범위를 표현함
    void set_total() {total_val = shares * share_val;}
    //private에서 함수 포함 가능

public: //public member 함수: private member의 값을 변경할 수 있는 단위
    void show();
    Stock& compare(Stock&);

    Stock(string, int, float); //입력이 있는 생성자
    // 기존의 acquire 함수 대체 //클래스 생성할 때 init하는 것들
    Stock(); //Default 생성자
};

void Stock::show() {
    cout << "회사 명 : " << name << endl;
    cout << "주식 수 : " << shares << endl;
    cout << "주가 : " << share_val << endl;
    cout << "주식 총 가치 : " << total_val << endl << endl;
}

Stock& Stock::compare(Stock &stock){
    if(stock.total_val >= total_val)
        return stock;
    else return *this;
}

//Stock 클래스 내부에서 귀속되어 있음
/* 생성자 */
Stock::Stock(string co, int n, float pr){
    name = co;
    shares = n;
    share_val = pr;
    set_total();
}
/* Default 생성자 */
Stock::Stock(){
    name = "None";
    shares = 0;
    share_val = 0;
    set_total();
}
#endif //FIRE_C_STOCK_STRUCTURE_H

 

728x90

+ Recent posts