[C++] Inheritance & Virtual function

choohaΒ·2026λ…„ 1μ›” 5일

C++

λͺ©λ‘ 보기
14/22

πŸ“š C++ 상속과 가상 ν•¨μˆ˜

πŸ”Έ 상속 (Inheritance)

μƒμ†μ˜ λͺ©μ 
1. Class Relationship (클래슀 관계 ν‘œν˜„)

  • "is-a" 관계λ₯Ό μ½”λ“œλ‘œ ν‘œν˜„
  • 예: Cat is an Animal, Dog is an Animal
  1. Code Reuse (μ½”λ“œ μž¬μ‚¬μš©)

    • 곡톡 κΈ°λŠ₯을 λΆ€λͺ¨ ν΄λž˜μŠ€μ— κ΅¬ν˜„
    • μžμ‹ ν΄λž˜μŠ€μ—μ„œ 쀑볡 μ½”λ“œ 제거
  2. Class Interface Consistency (μΌκ΄€λœ μΈν„°νŽ˜μ΄μŠ€)

    • 같은 λΆ€λͺ¨λ₯Ό 상속받은 ν΄λž˜μŠ€λ“€μ€ λ™μΌν•œ μΈν„°νŽ˜μ΄μŠ€ 제곡
    • λ‹€ν˜•μ„±(Polymorphism) κ΅¬ν˜„μ˜ 기반

πŸ”Έ 상속 μ ‘κ·Ό μ§€μ •μž (public/protected/private)

μ ‘κ·Ό μ§€μ •μžμ— λ”°λ₯Έ 멀버 μ ‘κ·Ό κΆŒν•œ λ³€ν™”

class Base
{
public:
    int x;
protected:
    int y;
private:
    int z;
};

1. public 상속 (κ°€μž₯ 일반적)

class Derived : public Base
{
    // xλŠ” public μœ μ§€
    // yλŠ” protected μœ μ§€
    // zλŠ” μ ‘κ·Ό λΆˆκ°€ (private은 항상 μ ‘κ·Ό λΆˆκ°€)
};
  • λΆ€λͺ¨ 클래슀의 μ ‘κ·Ό μ§€μ •μžλ₯Ό κ·ΈλŒ€λ‘œ μœ μ§€
  • "is-a" 관계λ₯Ό ν‘œν˜„ν•  λ•Œ μ‚¬μš©
  • λŒ€λΆ€λΆ„μ˜ 경우 public 상속을 μ‚¬μš©ν•¨

2. protected 상속 (λ“œλ¬Όκ²Œ μ‚¬μš©)

class Derived : protected Base
{
    // xλŠ” protected둜 λ³€κ²½
    // yλŠ” protected μœ μ§€
    // zλŠ” μ ‘κ·Ό λΆˆκ°€
};
  • λΆ€λͺ¨μ˜ public 멀버가 protected둜 λ³€κ²½
  • μ™ΈλΆ€μ—μ„œ λΆ€λͺ¨ 클래슀의 μΈν„°νŽ˜μ΄μŠ€ μ ‘κ·Ό 차단
  • "implemented-in-terms-of" 관계

3. private 상속 (클래슀의 κΈ°λ³Έκ°’)

class Derived : private Base  // classλŠ” 기본값이 private
{
    // xλŠ” private둜 λ³€κ²½
    // yλŠ” private둜 λ³€κ²½
    // zλŠ” μ ‘κ·Ό λΆˆκ°€
};
  • λͺ¨λ“  상속받은 멀버가 private둜 λ³€κ²½
  • κ΅¬ν˜„ 상속일 λ•Œ μ‚¬μš© (μΈν„°νŽ˜μ΄μŠ€ 상속이 μ•„λ‹˜)
  • 일반적으둜 Composition(ꡬ성)을 μ‚¬μš©ν•˜λŠ” 것이 더 λͺ…확함

μ ‘κ·Ό κΆŒν•œ 정리 ν‘œ

λΆ€λͺ¨ 클래슀public 상속protected 상속private 상속
publicpublicprotectedprivate
protectedprotectedprotectedprivate
privateμ ‘κ·Ό λΆˆκ°€μ ‘κ·Ό λΆˆκ°€μ ‘κ·Ό λΆˆκ°€

μ‹€μ „ κ°€μ΄λ“œλΌμΈ

  • 99%의 경우 public 상속을 μ‚¬μš©
  • protected/private 상속은 νŠΉμˆ˜ν•œ κ²½μš°μ—λ§Œ μ‚¬μš©
  • private 상속 λŒ€μ‹  멀버 λ³€μˆ˜λ‘œ 포함(Composition)ν•˜λŠ” 것을 ꢌμž₯
// ❌ Private 상속 (ν˜Όλž€μŠ€λŸ¬μ›€)
class Car : private Engine { };

// βœ… Composition (λͺ…확함)
class Car {
    Engine engine;  // Car has an Engine
};

πŸ”Έ Virtual Function (가상 ν•¨μˆ˜)

가상 ν•¨μˆ˜κ°€ ν•„μš”ν•œ 이유

class Animal
{
public:
    void speak()  // 일반 ν•¨μˆ˜
    {
        std::cout << "Animal sound" << std::endl;
    }
};

class Cat : public Animal
{
public:
    void speak()  // ν•¨μˆ˜ μž¬μ •μ˜ (Overriding)
    {
        std::cout << "Meow~" << std::endl;
    }
};

int main()
{
    Cat kitty;
    Animal* ptr = &kitty;  // λΆ€λͺ¨ νƒ€μž… ν¬μΈν„°λ‘œ μžμ‹ 객체 μ°Έμ‘°
    
    ptr->speak();  // ❌ "Animal sound" 좜λ ₯ (Cat의 speakκ°€ μ•„λ‹˜!)
    return 0;
}

문제점: 포인터 νƒ€μž…μ΄ Animal*μ΄λ―€λ‘œ 컴파일 νƒ€μž„μ— Animal::speak()κ°€ 호좜됨

virtual ν‚€μ›Œλ“œλ‘œ ν•΄κ²°

class Animal
{
public:
    virtual void speak()  // βœ… 가상 ν•¨μˆ˜λ‘œ μ„ μ–Έ
    {
        std::cout << "Animal sound" << std::endl;
    }
};

class Cat : public Animal
{
public:
    void speak() override  // override ν‚€μ›Œλ“œ ꢌμž₯
    {
        std::cout << "Meow~" << std::endl;
    }
};

int main()
{
    Cat kitty;
    Animal* ptr = &kitty;
    
    ptr->speak();  // βœ… "Meow~" 좜λ ₯ (λŸ°νƒ€μž„μ— μ‹€μ œ νƒ€μž… 확인!)
    return 0;
}

Dynamic Binding (동적 바인딩)

  • 가상 ν•¨μˆ˜λŠ” λŸ°νƒ€μž„μ— μ‹€μ œ 객체의 νƒ€μž…μ„ ν™•μΈν•˜μ—¬ 호좜
  • 이λ₯Ό 톡해 λ‹€ν˜•μ„±(Polymorphism) κ΅¬ν˜„

πŸ”Έ Virtual Destructor (가상 μ†Œλ©Έμž)

❗ μ€‘μš”: Base 클래슀의 μ†Œλ©ΈμžλŠ” λ°˜λ“œμ‹œ virtual둜 μ„ μ–Έν•΄μ•Ό 함

문제 상황

class Animal
{
public:
    Animal() { std::cout << "Animal constructor" << std::endl; }
    ~Animal() { std::cout << "Animal destructor" << std::endl; }  // ❌ 가상 μ†Œλ©Έμžκ°€ μ•„λ‹˜
};

class Cat : public Animal
{
public:
    Cat() { std::cout << "Cat constructor" << std::endl; }
    ~Cat() { std::cout << "Cat destructor" << std::endl; }
};

int main()
{
    Animal* polyCat = new Cat();
    // 좜λ ₯:
    // Animal constructor
    // Cat constructor
    
    delete polyCat;
    // 좜λ ₯:
    // Animal destructor  ❌ Cat destructorκ°€ ν˜ΈμΆœλ˜μ§€ μ•ŠμŒ!
    // β†’ λ©”λͺ¨λ¦¬ λˆ„μˆ˜ λ°œμƒ!
    
    return 0;
}

ν•΄κ²°: 가상 μ†Œλ©Έμž μ‚¬μš©

class Animal
{
public:
    Animal() { std::cout << "Animal constructor" << std::endl; }
    virtual ~Animal() { std::cout << "Animal destructor" << std::endl; }  // βœ… virtual μΆ”κ°€
};

class Cat : public Animal
{
public:
    Cat() { std::cout << "Cat constructor" << std::endl; }
    ~Cat() override { std::cout << "Cat destructor" << std::endl; }
};

int main()
{
    Animal* polyCat = new Cat();
    delete polyCat;
    // 좜λ ₯:
    // Cat destructor    βœ… μ˜¬λ°”λ₯Έ μˆœμ„œλ‘œ 호좜됨
    // Animal destructor
    
    return 0;
}

가상 μ†Œλ©Έμž κ·œμΉ™

  • 상속받을 κ°€λŠ₯성이 μžˆλŠ” 클래슀의 μ†Œλ©ΈμžλŠ” λ°˜λ“œμ‹œ virtual public으둜 μ„ μ–Έ
  • λ˜λŠ” 상속을 λ§‰μœΌλ €λ©΄ μ†Œλ©Έμžλ₯Ό protected둜 μ„ μ–Έ
// βœ… μ˜΅μ…˜ 1: 상속 ν—ˆμš© + virtual μ†Œλ©Έμž
class Base {
public:
    virtual ~Base() = default;
};

// βœ… μ˜΅μ…˜ 2: 상속 κΈˆμ§€ (μ†Œλ©Έμž protected)
class Base {
protected:
    ~Base() = default;  // μ™ΈλΆ€μ—μ„œ delete λΆˆκ°€
};

// βœ… μ˜΅μ…˜ 3: C++11 final ν‚€μ›Œλ“œ
class Base final {  // 상속 λΆˆκ°€
public:
    ~Base() = default;
};

πŸ”Έ Virtual Table (가상 ν•¨μˆ˜ ν…Œμ΄λΈ”)

λ©”λͺ¨λ¦¬ ꡬ쑰 λ³€ν™”

일반 ν•¨μˆ˜λ§Œ μžˆλŠ” 경우

class Animal
{
public:
    void speak() { std::cout << "Animal" << std::endl; }
private:
    double height;  // 8 bytes
};

class Cat : public Animal
{
public:
    void speak() { std::cout << "Meow~" << std::endl; }
private:
    double weight;  // 8 bytes
};

// sizeof(Animal) = 8 bytes  (height만)
// sizeof(Cat) = 16 bytes    (height + weight)

가상 ν•¨μˆ˜κ°€ μžˆλŠ” 경우

class Animal
{
public:
    virtual void speak() { std::cout << "Animal" << std::endl; }
private:
    double height;  // 8 bytes
};

class Cat : public Animal
{
public:
    void speak() override { std::cout << "Meow~" << std::endl; }
private:
    double weight;  // 8 bytes
};

// sizeof(Animal) = 16 bytes  (vptr 8 + height 8)
// sizeof(Cat) = 24 bytes     (vptr 8 + height 8 + weight 8)

Virtual Table (VTable) ꡬ쑰

Animal 객체 λ©”λͺ¨λ¦¬:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ vptr (8B)  β”‚ β†’ Animal VTable
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ height (8B)β”‚   β”‚&Animal::speak()β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Cat 객체 λ©”λͺ¨λ¦¬:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ vptr (8B)  β”‚ β†’ Cat VTable
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ height (8B)β”‚   β”‚ &Cat::speak()  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ weight (8B)β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Virtual Table의 λ™μž‘ 원리
1. 가상 ν•¨μˆ˜κ°€ μžˆλŠ” ν΄λž˜μŠ€λŠ” vptr (virtual table pointer) 을 가짐 (8 bytes)
2. vptr은 ν•΄λ‹Ή 클래슀의 VTable을 가리킴
3. VTableμ—λŠ” 가상 ν•¨μˆ˜λ“€μ˜ μ‹€μ œ μ£Όμ†Œκ°€ μ €μž₯됨
4. 가상 ν•¨μˆ˜ 호좜 μ‹œ vptr β†’ VTable β†’ μ‹€μ œ ν•¨μˆ˜ μ£Όμ†Œλ‘œ κ°„μ ‘ 호좜

μ„±λŠ₯ 고렀사항

  • 가상 ν•¨μˆ˜κ°€ μ—†λŠ” 경우: 직접 호좜 (빠름)
  • 가상 ν•¨μˆ˜κ°€ μžˆλŠ” 경우: κ°„μ ‘ 호좜 (μ•½κ°„ 느림, μ•½ 20-30% μ˜€λ²„ν—€λ“œ)
  • λ©”λͺ¨λ¦¬: 객체당 vptr 8 bytes μΆ”κ°€

πŸ”Έ λΆ€λͺ¨ 클래슀 ν¬μΈν„°μ˜ μ œμ•½

class Animal
{
public:
    virtual void speak() { std::cout << "Animal" << std::endl; }
};

class Cat : public Animal
{
public:
    void speak() override { std::cout << "Meow~" << std::endl; }
    void meow() { std::cout << "Meow meow!" << std::endl; }  // Cat μ „μš© ν•¨μˆ˜
};

int main()
{
    Cat kitty;
    Animal* ptr = &kitty;
    
    ptr->speak();  // βœ… OK - 가상 ν•¨μˆ˜μ΄λ―€λ‘œ Cat::speak() 호좜됨
    ptr->meow();   // ❌ 컴파일 μ—λŸ¬! Animal νƒ€μž…μœΌλ‘œλŠ” meow() μ ‘κ·Ό λΆˆκ°€
    
    // ν•΄κ²° 방법 1: Cat νƒ€μž… 포인터 μ‚¬μš©
    Cat* catPtr = &kitty;
    catPtr->meow();  // βœ… OK
    
    // ν•΄κ²° 방법 2: λ‹€μš΄μΊμŠ€νŒ… (ꢌμž₯ν•˜μ§€ μ•ŠμŒ)
    Cat* catPtr2 = static_cast<Cat*>(ptr);
    catPtr2->meow();  // βœ… OK (ν•˜μ§€λ§Œ μœ„ν—˜ν•¨)
    
    return 0;
}

핡심 원칙

  • λΆ€λͺ¨ νƒ€μž… ν¬μΈν„°λ‘œλŠ” λΆ€λͺ¨ ν΄λž˜μŠ€μ— μ„ μ–Έλœ λ©€λ²„λ§Œ μ ‘κ·Ό κ°€λŠ₯
  • 가상 ν•¨μˆ˜λŠ” λŸ°νƒ€μž„μ— μ‹€μ œ νƒ€μž…μ˜ ν•¨μˆ˜κ°€ ν˜ΈμΆœλ˜μ§€λ§Œ, μ ‘κ·Ό κ°€λŠ₯ μ—¬λΆ€λŠ” 컴파일 νƒ€μž„μ— 결정됨
  • μžμ‹ 클래슀 μ „μš© ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λ €λ©΄ μžμ‹ νƒ€μž… 포인터λ₯Ό μ‚¬μš©ν•΄μ•Ό 함

πŸ”Έ Pure Virtual Function (순수 가상 ν•¨μˆ˜)

순수 가상 ν•¨μˆ˜ μ„ μ–Έ

class Animal
{
public:
    virtual void speak() = 0;  // βœ… 순수 가상 ν•¨μˆ˜
    // = 0 은 "κ΅¬ν˜„μ΄ μ—†μŒ"을 의미
};

Abstract Class (좔상 클래슀)

  • 순수 가상 ν•¨μˆ˜κ°€ ν•˜λ‚˜λΌλ„ μžˆλŠ” 클래슀λ₯Ό 좔상 클래슀라고 함
  • 좔상 ν΄λž˜μŠ€λŠ” 객체λ₯Ό 생성할 수 μ—†μŒ
  • 좔상 클래슀λ₯Ό 상속받은 ν΄λž˜μŠ€λŠ” λͺ¨λ“  순수 가상 ν•¨μˆ˜λ₯Ό κ΅¬ν˜„ν•΄μ•Ό 객체 생성 κ°€λŠ₯
class Animal  // Abstract class
{
public:
    virtual void speak() = 0;  // 순수 가상 ν•¨μˆ˜
    virtual void eat() { std::cout << "Eating" << std::endl; }  // 일반 가상 ν•¨μˆ˜λ„ κ°€λŠ₯
    virtual ~Animal() = default;
};

class Cat : public Animal
{
public:
    void speak() override { std::cout << "Meow~" << std::endl; }  // βœ… κ΅¬ν˜„ ν•„μˆ˜
};

class Dog : public Animal
{
    // ❌ speak()λ₯Ό κ΅¬ν˜„ν•˜μ§€ μ•ŠμŒ β†’ Dog도 좔상 ν΄λž˜μŠ€κ°€ 됨
};

int main()
{
    Animal animal;  // ❌ 컴파일 μ—λŸ¬! 좔상 ν΄λž˜μŠ€λŠ” 객체 생성 λΆˆκ°€
    Cat kitty;      // βœ… OK - speak()λ₯Ό κ΅¬ν˜„ν–ˆμœΌλ―€λ‘œ
    Dog puppy;      // ❌ 컴파일 μ—λŸ¬! Dog도 좔상 클래슀
    
    Animal* ptr = new Cat();  // βœ… OK - ν¬μΈν„°λŠ” κ°€λŠ₯
    ptr->speak();  // "Meow~"
    delete ptr;
    
    return 0;
}

πŸ”Έ Interface (μΈν„°νŽ˜μ΄μŠ€)

μΈν„°νŽ˜μ΄μŠ€ μ •μ˜

  • 순수 가상 ν•¨μˆ˜λ§ŒμœΌλ‘œ 이루어진 클래슀
  • κ΅¬ν˜„(implementation)κ³Ό 멀버 λ³€μˆ˜κ°€ μ—†μŒ
  • λ‹€λ₯Έ μ–Έμ–΄(Java, C#)의 interface와 λ™μΌν•œ κ°œλ…
// βœ… 쒋은 μΈν„°νŽ˜μ΄μŠ€ 섀계
class IDrawable  // I μ ‘λ‘μ‚¬λŠ” μΈν„°νŽ˜μ΄μŠ€ ν‘œμ‹œ (κ΄€λ‘€)
{
public:
    virtual void draw() = 0;
    virtual void setColor(int r, int g, int b) = 0;
    virtual ~IDrawable() = default;  // 가상 μ†Œλ©ΈμžλŠ” ν•„μˆ˜
    
    // 멀버 λ³€μˆ˜ μ—†μŒ
    // κ΅¬ν˜„λœ ν•¨μˆ˜ μ—†μŒ
};

class Circle : public IDrawable
{
public:
    void draw() override { /* 원 그리기 κ΅¬ν˜„ */ }
    void setColor(int r, int g, int b) override { /* 색상 μ„€μ • κ΅¬ν˜„ */ }
    
private:
    double radius;
    int red, green, blue;
};

μΈν„°νŽ˜μ΄μŠ€ 섀계 원칙

  1. 순수 가상 ν•¨μˆ˜λ§Œ 포함
    • κ΅¬ν˜„ μ½”λ“œ μ—†μŒ
    • 멀버 λ³€μˆ˜ μ—†μŒ
  2. 가상 μ†Œλ©Έμž ν•„μˆ˜
    virtual ~IDrawable() = default;
  3. 볡사/이동 λ°©μ§€ (ꢌμž₯)
    class IDrawable
    {
    public:
        virtual void draw() = 0;
        virtual ~IDrawable() = default;
        
    protected:
        IDrawable() = default;  // μƒμ„±μž protected
        IDrawable(const IDrawable&) = delete;  // 볡사 κΈˆμ§€
        IDrawable& operator=(const IDrawable&) = delete;
    };

μΈν„°νŽ˜μ΄μŠ€ μ‚¬μš©μ˜ μž₯점

  • μœ μ§€λ³΄μˆ˜μ„± ν–₯상: κ΅¬ν˜„κ³Ό μΈν„°νŽ˜μ΄μŠ€ 뢄리
  • μ˜μ‘΄μ„± κ°μ†Œ: ꡬ체적인 클래슀 λŒ€μ‹  μΈν„°νŽ˜μ΄μŠ€μ— 의쑴
  • ν…ŒμŠ€νŠΈ 용이: Mock 객체 생성 쉬움
  • ν”ŒλŸ¬κ·ΈμΈ μ•„ν‚€ν…μ²˜: λŸ°νƒ€μž„μ— κ΅¬ν˜„μ²΄ ꡐ체 κ°€λŠ₯
// λ‚˜μœ 섀계: ꡬ체 ν΄λž˜μŠ€μ— 의쑴
void processImage(JPEGImage& img) { /* ... */ }

// 쒋은 섀계: μΈν„°νŽ˜μ΄μŠ€μ— 의쑴
void processImage(IImage& img) { /* ... */ }
// JPEG, PNG, BMP λ“± λ‹€μ–‘ν•œ κ΅¬ν˜„μ²΄ μ‚¬μš© κ°€λŠ₯

μ‹€μ „ 예제: 닀쀑 μΈν„°νŽ˜μ΄μŠ€ κ΅¬ν˜„

class ISerializable
{
public:
    virtual std::string serialize() = 0;
    virtual void deserialize(const std::string& data) = 0;
    virtual ~ISerializable() = default;
};

class ILoggable
{
public:
    virtual void log() = 0;
    virtual ~ILoggable() = default;
};

class User : public ISerializable, public ILoggable
{
public:
    std::string serialize() override { /* JSON λ³€ν™˜ */ }
    void deserialize(const std::string& data) override { /* JSON νŒŒμ‹± */ }
    void log() override { /* 둜그 좜λ ₯ */ }
    
private:
    std::string name;
    int age;
};

πŸ”Έ 핡심 정리

상속 μ‚¬μš© κ°€μ΄λ“œλΌμΈ
1. public μƒμ†λ§Œ μ‚¬μš© (99%의 경우)
2. Base 클래슀 μ†Œλ©ΈμžλŠ” virtual public λ˜λŠ” protected
3. λ‹€ν˜•μ„±μ΄ ν•„μš”ν•˜λ©΄ virtual ν•¨μˆ˜ μ‚¬μš©
4. μΈν„°νŽ˜μ΄μŠ€λŠ” 순수 가상 ν•¨μˆ˜λ§ŒμœΌλ‘œ ꡬ성
5. override ν‚€μ›Œλ“œλ‘œ μ˜λ„ λͺ…ν™•νžˆ ν‘œν˜„

// βœ… μ˜¬λ°”λ₯Έ 상속 ꡬ쑰
class IAnimal  // μΈν„°νŽ˜μ΄μŠ€
{
public:
    virtual void speak() = 0;
    virtual ~IAnimal() = default;
};

class Animal : public IAnimal  // 좔상 클래슀
{
public:
    void speak() override { std::cout << "Animal" << std::endl; }
    virtual ~Animal() = default;
    
protected:
    int age;  // 곡톡 데이터
};

class Cat final : public Animal  // ꡬ체 클래슀 (final둜 더 이상 상속 κΈˆμ§€)
{
public:
    void speak() override { std::cout << "Meow~" << std::endl; }
};

<참고 자료>

μ½”λ“œμ—†λŠ” ν”„λ‘œκ·Έλž˜λ°
C++ Inheritance
C++ Virtual Functions

0개의 λŒ“κΈ€