[GeeksForGeeks C++ 문제풀이] Class and Object(클래스와 객체)

Jin Hur·2022년 10월 25일
0

C++

목록 보기
16/18

https://www.geeksforgeeks.org/c-plus-plus-gq/class-and-object-gq/

Q1. 구조체와 클래스 차이

C++에서 구조체는 클래스와 동일한 방식으로 작동한다.
하지만 사용하는 철학에 따라 차이를 보인다. 바로 '구현 세부 정보'를 공개하느냐 숨기느냐 차이이다.

구조체는 기본적으로 사용자에게 구현 세부 정보를 숨기지 않는 반면, 클래스는 기본적으로 숨겨서 액세스를 방지한다.

source: https://www.geeksforgeeks.org/structure-vs-class-in-cpp/

클래스는 기본적으로 데이터를 숨기고(캡슐화), 데이터를 처리하는 로직을 감추어 특정 기능을 public 함수로 제공하는 반면, 구조체는 데이터들의 집합을 다루기 위해 사용한다.


Q2. sizeof(empty class)

C언어에 구조체가 도입되었을 땐 객체의 개념이 없었다. 따라서 빈 구조체의 크기를 0으로 유지할 수 있었다.

그러나 C++에서 구조체/클래스의 크기는 함수 호출을 위해 적어도 1바이트(최소 바이트) 이상을 가져야하며, 빈 구조체/클래스의 크기는 1바이트이다.

	struct EmptyStruct {

	};
	class EmptyClass {

	};

	{
		cout << sizeof(EmptyStruct) << endl;	// => 0
		cout << sizeof(EmptyClass) << endl;		// => 0
	}

빈 클래스(empty class): 빈 클래스란 데이터 멤버를 갖지않는 클래스를 의미한다. 멤버 함수의 유무는 따지지 않는다.

단순히 객체가 없는 클래스에는 할당된 공간이 필요하지 않는다. 공간은 클래스가 객체화 될 때 할당되므로 컴파일러는 고유 주소 식별을 위해 빈 클래스 객체에 적어도 1바이트 이상을 할당한다.

적어도 1바이트만큼이라도 공간을 할당받아야지만 주소를 가질 수 있게 되고, 주소를 가진 각 객체는 서로 구별될 수 있다.

// Creating an Empty class
class Empty {
};
 
// Driver Code
int main()
{
    Empty a, b;
 
    if (&a == &b)
        cout << "Impossible " << endl;
    else
        cout << "Fine " << endl;
        // "Fine" 출력~!
 
    return 0;
}

한 가지 주의할 점은 빈 클래스를 상속받았다고, 빈 클래스의 크기 1바이트가 추가되는 것은 아니다.

#include <iostream>
using namespace std;
 
// Creating an Empty Class
class Empty {
};
 
// Creating a Derived Class
class Derived : Empty {
    int a;
};
 
// Driver Code
int main()
{
    cout << sizeof(Derived);	// => 4
    return 0;
}

Q3. 기본 접근 지정자


Q4. 객체들이 공유하는 것

같은 클래스형 객체들은 비정적 데이터 멤버를 공유하지 않는다. 각 객체의 주소에 존재하는 고유한 공간을 가진다. 반면 정적 데이터 멤버는 공유하고, 각 객체의 공간에 포함되지 않는다(Data 영역에 존재).

비정적 멤버 함수의 경우 객체들이 공유한다.


Q5. 객체, 포인터의 크기 (with static 변수)

32비트 시스템에서 포인터의 크기는 4바이트,
64비트 시스템에서 포인터의 크기는 8바이트이다.

32비트 시스템이라고 가정하였을 때, sizeof(Test*)는 4이고(Test 클래스 포인터 크기는 4바이트), sizeof(t)는 객체의 크기를 의미하므로 8(바이트)이다.

static 멤버는 각 객체의 공간에 포함되지 않기에 12(바이트)는 오답이다.


Q6. 전역변수와 지역변수

기본 자료형 변수와 마찬가지로 이름이 같고 범위가 다른 2개의 객체를 생성할 수 있다.

같은 범위, 가까운 범위에 선언된 변수가 우선되기에,
t.get();
cout << .. << t.i << ..; 에서는 지역변수가 참조되고,
::t.get();
cout << .. << ::t.i << ..; 과 같이 :: 전역 변수 연산자를 통해 전역변수를 참조할 수 있다.


Q7. 멤버함수가 접근할 수 있는 데이터


Q8. 가상함수 특징

  • 가상함수는 static으로 선언할 수 없다.
    * static virtual void func(); // 컴파일 에러
  • 가상함수는 반드시 public 접근 지정자 제한할 필요는 없다.

Q9. 클래스 탬플릿, 함수 템플릿


Q10. 함수에 인자로 전달할 수 없는 것


Q11. Association, Composition

의존(dependency)

  • 어떤 클래스가 다른 클래스를 참조
  • 참조의 형태: 메서드내에서 대상 클래스의 (1)객체 생성/ (2)객체 사용/(3)메서드 호출/(4)객체 리턴/(5)매개변수로 객체 받기

연관(Association)

다른 객체를 멤버로 가지는 관계면 연관 관계라 할 수 있다.

집합(Aggregation), 연관의 특수 표기(1)

멤버 객체와 whole(전체)와 part(부분)의 관계를 가진다.

합성(Composition), 연관의 특수 표기(2)

집합과 마찬가지로 whole(전체)와 part(부분)의 집합 관계를 나타내지만 보다 더 강한 집합을 의미

강한 집합이란 뜻은 part가 whole에 종속적이어서 part가 whole의 소유란 의미.

  • (1) part를 가지는 whole 인스턴스가 part 인스턴스의 전체 수명을 책임
    • whole 인스턴스가 part 인스턴스 생성
    • whole 인스턴스가 소멸되면 part 인스턴스도 소멸
    • whole 인스턴스가 복사되면 part 인스턴스도 함께 복사 (깊은 복사)
  • (2) part에 해당하는 인스턴스는 공유될 수 없음

반대로 약한 집합은 part가 whole에 대해 독립적이어서 whole이 part를 빌려 쓰는 것과 비슷함.

인스턴스가 공유되는 경우

인스턴스가 공유되지 않는 경우


Q12. 추상 클래스

추상 클래스 멤버도 초기화 할 수 있다.

예시

class Base {
private:
	string name;

public:
	Base(string s) {
		name = s;
	}

	// 순수 가상함수
	virtual void pureVirtualFunc() = 0;

	void printInfo() {
		cout << "name: " << name << endl;
	}
};

class Derived : public Base {
private:
	int age;

public:
	Derived(string name, int age) : Base(name), age(age) 
	{}

	// 순수가상함수 오버라이딩
	void pureVirtualFunc() override {
		cout << "override func()" << endl;
	}

	void printInfo() {
		Base::printInfo();
		cout << "age: " << age << endl;
	}
};

int main() {
	Derived d("jin", 28);

	d.pureVirtualFunc();
	d.printInfo();
}


Q13. redefine? override?

  • redefine: 조상 클래스의 비가상함수를 재정의하는 것이다. 컴파일 시점에 호출 함수가 정해지므로 정적 바인딩이 예상된다.
  • override: 조상 클래스의 가상함수를 재정의하는 것이다. 런타임 시점에 호출 함수가 정해지므로 동적 바인딩이 예상된다.


Q16. 기본 클래스에서 파생 클래스로 상속되는 것


Q17. 프렌드 함수는 클래스의 멤버가 아니다

0개의 댓글