C에서 스택에 "클래스"를 만들고 있습니까?
C "class"(C "class"에 포인터를 첫 번째 인수로 사용하는 함수에 액세스하여 사용되는 구조)를 볼 때마다 다음과 같이 구현되어 있는 것을 볼 수 있습니다.
typedef struct
{
int member_a;
float member_b;
} CClass;
CClass* CClass_create();
void CClass_destroy(CClass *self);
void CClass_someFunction(CClass *self, ...);
...
이 , 「 」는 「 」입니다.CClass_create
★malloc
메모리이며 포인터를 반환합니다.
new
불필요하게 C++로 올라오는 것은 보통 C++ 프로그래머들을 미치게 하는 것처럼 보이지만, 이 관행은 C에서 받아들여질 수 있는 것처럼 보인다.왜왜????히프 할당 구조 "클래스"가 일반화된 배경에는 어떤 이유가 있습니까?
여기에는 몇 가지 이유가 있습니다.
- "opaque" 포인터 사용
- 파괴자의 부족
- 임베디드 시스템(스택 오버플로우 문제)
- 컨테이너
- 관성
- "게으름"
간단히 논의해 봅시다.
불투명 포인터의 경우 다음과 같은 작업을 수행할 수 있습니다.
struct CClass_;
typedef struct CClass_ CClass;
// the rest as in your example
사용자는 , 저, 음, 음의 정의를 볼 수 struct CClass_
플랫폼마다 클래스를 다르게 구현하는 등 다른 흥미로운 작업을 가능하게 합니다.
스택 , 물 、 것 、 것 、 것 、 것 、 of 、 of 、 of of 。CClass
OTOH는 이렇게 OTOH를 하는 것이 금지되는 것은 을 알 수 CClass
- (풀에서)에 의해 됩니다.CClass_create
또 다른 이 있다, 이런 기능이 있을 수도 있어요.CClass_create_static
.
디스트럭터 부족 - C 컴파일러는 자동으로 디스트럭터를 파괴하지 않기 때문에CClass
스택 오브젝트는 사용자가 직접 실행해야 합니다(파괴자 함수를 호출하지 않음).수 없이 속도가 수 OTOH를 사용하다풀이나 아레나 등에서 할당할 수 있습니다.스택 할당의 잠재적인 문제 없이 스택 할당과 거의 같은 속도로 할당할 수 있습니다.
임베디드 시스템 - 스택은 "무한" 리소스가 아닙니다.물론 오늘날의 "일반" OS(POSIX, Windows...)의 대부분의 애플리케이션은 거의 그렇습니다.다만, 임베디드 시스템에서는, 스택의 사이즈가 몇 KB 정도 되는 경우가 있습니다.이는 극단적이지만 "대형" 임베디드 시스템도 MB 단위의 스택을 가지고 있습니다.그래서 너무 많이 쓰면 바닥나요.이 경우, 대부분의 경우 무슨 일이 일어날지 장담할 수 없습니다.- AFIK는 C와 C++ 모두에서 "정의되지 않은 동작"입니다.OTOH,CClass_create()
메모리가 부족하면 NULL 포인터를 반환할 수 있습니다.이것에 대처할 수 있습니다.
컨테이너 - C++ 사용자는 스택 할당을 좋아하지만std::vector
스택에서는 그 내용이 힙으로 할당됩니다.물론 이를 조정할 수 있지만 이는 기본 동작이며, 컨테이너의 모든 멤버가 힙 할당되어 있지 않은 경우 처리 방법을 찾기보다는 "모든 멤버가 힙 할당되어 있다"라고 말하는 것이 훨씬 쉬워집니다.
관성 - 음, OO는 SmallTalk에서 나왔습니다.모든 것이 역동적이기 때문에 C로의 "자연적" 변환은 "모든 것을 힙 위에 올려놓기" 방식입니다.그래서 첫 번째 예시는 그런 것들이었고 여러 해 동안 다른 사람들에게 영감을 주었습니다.
"게으름" - 스택 객체만 원하는 경우 다음과 같은 것이 필요합니다.
CClass CClass_make();
void CClass_deinit(CClass *me);
단, 스택과 힙을 모두 허용하려면 다음을 추가해야 합니다.
CClass *CClass_create();
void CClass_destroy(CClass *me);
이는 구현자에게 더 많은 작업을 수행하지만 사용자에게도 혼란스러운 작업입니다.1개는 약간 다른 인터페이스를 만들 수 있지만, 2개의 기능이 필요하다는 사실은 변하지 않습니다.
물론 컨테이너의 이유도 부분적으로 게으름의 원인이다.
바와 , 「 」라고 가정하면CClass_create
★★★★★★★★★★★★★★★★★」CClass_destroy
malloc/free
그럼, 저는 다음 작업을 하는 것이 좋지 않습니다.
void Myfunc()
{
CClass* myinstance = CClass_create();
...
CClass_destroy(myinstance);
}
말록과 공짜를 쉽게 피할 수 있었기 때문입니다.
void Myfunc()
{
CClass myinstance; // no malloc needed here, myinstance is on the stack
CClass_Initialize(&myinstance);
...
CClass_Uninitialize(&myinstance);
// no free needed here because myinstance is on the stack
}
와 함께
CClass* CClass_create()
{
CClass *self= malloc(sizeof(CClass));
CClass_Initialize(self);
return self;
}
void CClass_destroy(CClass *self);
{
CClass_Uninitialize(self);
free(self);
}
void CClass_Initialize(CClass *self)
{
// initialize stuff
...
}
void CClass_Uninitialize(CClass *self);
{
// uninitialize stuff
...
}
C++ 에서는, 다음과 같이 하는 것이 좋습니다.
void Myfunc()
{
CClass myinstance;
...
}
이하와 같습니다.
void Myfunc()
{
CClass* myinstance = new CCLass;
...
delete myinstance;
}
한 '불필요한'을 new
/delete
.
C에서, 어떤 구성요소가 "만들기" 기능을 제공할 때, 구성요소 시행자는 구성요소가 초기화되는 방법을 통제한다.따라서 C++를 에뮬레이트할 뿐만 아니라operator new
클래스 컨스트럭터도 있습니다.
초기화에 대한 이러한 제어를 포기하는 것은 입력에 대한 오류 확인을 훨씬 더 많이 의미하므로, 제어를 유지하면 일관되고 예측 가능한 동작을 쉽게 제공할 수 있습니다.
합니다.malloc
메모리 할당에 항상 사용됩니다.이것은 종종 있는 일이지만 항상 그런 것은 아니다.예를 들어 일부 임베디드 시스템에서는malloc
/free
전혀 사용되지 않습니다.X_create
함수는 예를 들어 컴파일 시 크기가 고정된 배열에서 다른 방법으로 할당할 수 있습니다.
이것은 어느 정도 의견 기반이기 때문에 많은 답을 낳는다.그러나 개인적으로 "C 객체"를 힙에 할당하는 것을 선호하는 이유를 설명하겠습니다.그 이유는 코드를 소비하지 않도록 내 필드를 모두 숨기기 위해서입니다(speak: private).이것은 불투명 포인터라고 불립니다.실제로는 헤더 파일에 정의되어 있지 않습니다.struct
사용중에는 선언만 합니다.로서 소비 는 as적 、 can can 、 드 as 、 드 as 、 드 as 、 as as of of as as of of of of of of of of of of of'를 알 수 .struct
스택 할당이 불가능하게 됩니다.
장점은 코드 소비는 절대 코드의 정의에 의존할 수 없다는 것입니다.struct
그 수 struct
외부와 일관성이 없기 때문에 불필요한 코드 재컴파일을 피할 수 있습니다.struct
★★★★★★★★★★★★★★★★★★.
첫 번째 문제는 c++에서 필드를 다음과 같이 선언함으로써 해결됩니다.private
당신의 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★」class
Import만 다시.private
멤버가 바뀝니다.c++에서 자주 사용되는 솔루션은pimpl
pattern:를 두 패턴으로 한다: "Pattern" (프라이빗멤버) 1 "Pattern" (): "Pattern" (프라이빗멤버)struct
(복수:class
구현 파일에만 정의되어 있습니다.이것은 의 of, 것, 것이 합니다.pimpl
힙에 할당됩니다.
덧붙여: 현대의 OOP 언어(예를 들면, Java 나 c#)에는, 발신 코드가 그 정의를 인식하지 않고 오브젝트를 할당하는 수단(통상은 스택인지 힙인지를 내부적으로 판단)이 있습니다.
「」가 것.*
지금까지 것은 malloc
. d. 로의 수 static
글로벌 입니다.실제로,이, variable들,,,,,,,,,,,,.실로 、 신의 、CClass_destroy()
는 파괴되는 객체에 대한 일부 정보를 이미 알고 있다고 가정하는 파라미터를 사용하지 않습니다.
포인터는 ', '아니다', '아니다', '아니다', '아니다', '아니다', '아니다', '아니다', '아니다', '아니다', '아니다', '아니다', '아니다',malloc
d는 오브젝트를 변경할 수 있는 유일한 방법입니다.
스택 대신 히프를 사용하는 특별한 이유는 알 수 없습니다.사용하는 메모리가 줄어들지 않기 때문입니다.그러나 이러한 "클래스"를 초기화하기 위해 필요한 것은 init/destroy 함수입니다. 왜냐하면 기본 데이터 구조가 실제로 동적 데이터를 포함할 필요가 있기 때문입니다. 따라서 포인터의 사용이 필요합니다.
를 '시공자'로.void CClass_create(CClass*);
이것은 구조체의 인스턴스/참조를 반환하지 않고 1개로 호출됩니다.
「스택」에 할당되어 있는지, 또는 동적으로 할당되어 있는지에 대해서는, 사용 시나리오의 요건에 따라서 다릅니다.을 하든 그냥 '어느 쪽으로', '어느 쪽으로', '어느 쪽으로', '어느 쪽으로' 하면 .CClass_create()
이치노
{
CClass stk;
CClass_create(&stk);
CClass *dyn = malloc(sizeof(CClass));
CClass_create(dyn);
CClass_destroy(&stk); // the local object lifetime ends here, dyn lives on
}
// and later, assuming you kept track of dyn
CClass_destroy(dyn); // destructed
free(dyn); // deleted
참조를 로컬(스택에 할당)로 되돌리지 않도록 주의해 주세요.이것은 UB이기 때문입니다.
을 할당하든 간에, 이 번호를 불러야 .void CClass_destroy(CClass*);
(오브젝트의 라이프 타임의 끝)에, 동적으로 할당되어 있는 경우는, 그 메모리도 해방됩니다.
할당/할당 해제와 구축/파괴를 구분합니다(C++에서는 자동으로 결합될 수 있습니다).
함수는 할당된 다른 구조물에 대한 포인터가 없는 경우에만 스택 할당 구조를 반환할 수 있기 때문입니다.단순한 오브젝트(int, bool, floats, chars 및 배열)만을 포함하는 경우 스택에 할당할 수 있습니다.하지만 반납하면 복사된다는 걸 알아둬야 해요.다른 구조물에 대한 포인터를 허용하거나 복사를 피하려면 힙을 사용하십시오.
그러나 최상위 단위로 구조체를 작성하고 호출된 함수에서만 사용할 수 있고 반환되지 않는 경우 스택이 적합합니다.
동시에 존재할 필요가 있는 오브젝트의 최대 수가 정해져 있는 경우, 시스템은 모든 "라이브" 인스턴스에서 어떤 작업을 수행할 수 있어야 하며, 문제가 되는 아이템이 너무 많은 비용을 소비하지 않는 경우, 최선의 접근법은 일반적으로 히프 할당이나 스택 할당이 아닌 정적으로 할당된 어레이입니다."create" 및 "create" 메서드를 사용합니다.어레이를 사용하면 오브젝트의 링크 리스트를 유지할 필요가 없어집니다.또한 오브젝트가 비지 상태이기 때문에 오브젝트를 즉시 파기할 수 없는 경우(예를 들어 데이터가 인터럽트 또는 DMA 경유로 채널에 도달하고 있는 경우, 사용자 코드로 채널에 관심이 없다고 판단되어 폐기된 경우 등)를 처리할 수 있습니다."dispose when done" 플래그를 사용하여 더 이상 할당되지 않은 인터럽트 또는 DMA 덮어쓰기를 걱정할 필요가 없습니다.
고정 크기의 개체 풀을 사용하면 혼합 크기의 힙에서 스토리지를 가져오는 것보다 할당 및 할당 해제를 훨씬 쉽게 예측할 수 있습니다.수요가 가변적이고 객체가 많은 공간을 차지하는 경우(개별 또는 집합적으로)에는 이 접근방식은 적합하지 않지만, 수요가 거의 일정할 경우(예: 애플리케이션에 항상 12개의 객체가 필요하며 경우에 따라서는 최대 3개의 객체가 더 필요할 수 있음)에는 다른 접근방식보다 훨씬 효과적입니다.단점은 스태틱버퍼가 선언된 장소에서 셋업을 수행하거나 클라이언트의 실행 가능한 코드로 셋업을 수행해야 한다는 것입니다.클라이언트 사이트에서는 변수 초기화 구문을 사용할 수 없습니다.
덧붙여서, 이 어프로치를 사용하는 경우, 클라이언트 코드가 포인터를 수신하도록 할 필요는 없습니다.대신 편리한 크기의 정수를 사용하여 리소스를 식별할 수 있습니다.리소스 가 " "의 int
일부 상태 변수에서 리소스당1비트를 사용하는 것이 도움이 될 수 있습니다.를 들어, 변수에는 '아예'가 수 있습니다.timer_notifications
핸들러에 )및 ('인터럽트 핸들러에 의해서만 작성)timer_acks
) 및합니다.(timer_notifications ^ timer_acks)
타이머 N이 서비스를 원할 때마다 설정됩니다.이러한 접근방식을 사용하면 타이머마다 1개의 변수를 읽을 필요 없이 타이머가 서비스를 필요로 하는지 여부를 판단하기 위해 코드는 2개의 변수만 읽을 필요가 있습니다.
C에는 C++ 프로그래머가 당연하게 여기는 것이 결여되어 있습니다.
- 퍼블릭 및 프라이빗 지정자
- 컨스트럭터 및 디스트럭터
이 접근법의 큰 장점은 C 파일의 구조를 숨기고 생성 및 파괴 기능을 사용하여 올바른 구축 및 파괴를 강제할 수 있다는 것입니다.
.h 파일의 구조를 공개하면 사용자가 멤버에 직접 액세스 할 수 있어 캡슐화가 깨집니다.또한 강제로 생성하지 않으면 개체를 잘못 구성할 수 있습니다.
"C에서는 메모리를 동적으로 할당하는 것이 정상적인데 C++에서는 그렇지 않은 이유"가 질문입니까?
C++ 에는, 새로운 용장성을 실현하는 많은 구조가 준비되어 있습니다.복사, 이동 및 일반 생성자, 파괴자, 표준 라이브러리, 할당자.
하지만 C에서는 피할 수 없다.
사실 C++가 "새로운"을 너무 쉽게 만드는 것에 대한 반발이다.
이론적으로 C에서 이 클래스 구성 패턴을 사용하는 것은 C++에서 "new"를 사용하는 것과 같으므로 차이가 없습니다.하지만 사람들이 언어에 대해 생각하는 방식이 다르기 때문에 사람들이 코드에 반응하는 방식이 다릅니다.
C에서는, 목표를 달성하기 위해서 컴퓨터가 실시할 필요가 있는 정확한 조작에 대해 생각하는 것이 매우 일반적입니다.보편적이지는 않지만, 매우 일반적인 사고방식입니다.malloc/free의 비용/편익 분석에 시간을 들였다고 가정합니다.
C++에서는, 자신도 모르는 사이에, 큰 도움이 되는 코드 라인을 작성하는 것이 훨씬 쉬워졌습니다.코드를 한 줄 써놓고 100명, 200명의 신규/삭제자가 필요하게 된 것도 모르는 사람이 흔하다.이 때문에 C++ 개발자는 뉴스에 광적으로 트집잡고 삭제해 여기저기서 실수로 불릴 것을 우려해 역풍을 일으켰다.
물론 이것은 일반화입니다.C 및 C++ 커뮤니티 전체가 이러한 금형에 적합한 것은 아닙니다.그러나 힙에 물건을 올려놓지 않고 새로운 것을 사용하는 것에 불만을 느끼고 있는 경우는 이것이 근본 원인일 수 있습니다.
네가 그것을 그렇게 자주 보다니 좀 이상하다.당신은 분명 어떤 "게으른" 코드처럼 보였을 거예요.
C에서 설명하는 기술은 일반적으로 라이브러리 유형, 즉 클라이언트의 코드에 의도적으로 보이지 않게 정의된 구조 유형으로 "불투명"하도록 예약되어 있습니다.클라이언트는 이러한 오브젝트를 선언할 수 없기 때문에, 「숨겨진」라이브러리 코드의 동적 할당에 대해서, 이디옴을 사용할 필요가 있습니다.
구조의 정의를 숨길 필요가 없는 경우 일반적인 C 관용구는 보통 다음과 같습니다.
typedef struct CClass
{
int member_a;
float member_b;
} CClass;
CClass* CClass_init(CClass* cclass);
void CClass_release(CClass* cclass);
★★CClass_init
는 을 합니다.*cclass
및 resultobject와 가 , 오브젝트에 에 대한 에게 있으며 하다고 생각되는 할 수 있습니다.
CClass cclass;
CClass_init(&cclass);
...
CClass_release(&cclass);
이 고사성어입니다.pthread_mutex_t
pthread_mutex_init
★★★★★★★★★★★★★★★★★」pthread_mutex_destroy
.
한편, (원래 코드와 같이) 비투명 타입에 대해 앞의 기술을 사용하는 것은 일반적으로 의문스러운 방법입니다.C++에서 다이내믹 메모리를 불필요하게 사용하는 것은 매우 의문입니다.동작합니다만, 다이나믹 메모리를 불필요하게 사용하는 것은, C++ 와 같이, C 로부터의 사용이 거부되고 있습니다.
언급URL : https://stackoverflow.com/questions/31673065/creating-classes-in-c-on-the-stack-vs-the-heap
'programing' 카테고리의 다른 글
Java에서의 휘발성 vs 스태틱 (0) | 2022.08.07 |
---|---|
webpack 또는 browserify를 사용하지 않고 .vue 파일을 .js 파일로 컴파일합니다. (0) | 2022.08.07 |
Vuex 스토어에서 알 수 없는 로컬 작업 유형을 가져오는 중 (0) | 2022.08.07 |
첫 번째 로드에서만 mapGetters의 반응성 문제 (0) | 2022.08.07 |
개발에서는 작동하지만 Prod에서는 작동하지 않는 Vue JS에서 요소를 제거할 때의 애니메이션 (0) | 2022.08.07 |