C ++ '새로운'연산자 문제?
-
09-09-2019 - |
문제
나는 최근에 만났다 이 폭언.
나는이 기사에 언급 된 몇 가지 요점을 잘 이해하지 못한다.
- 저자는 작은 성가심을 언급합니다
delete
vsdelete[]
, 그러나 솔루션을 제공하지 않고 실제로 (컴파일러를 위해) 실제로 필요하다고 주장하는 것 같습니다. 내가 뭐 놓친 거 없니? '특수 할당 자'섹션에서 기능
f()
, 할당을 다음과 같이 대체하면 문제를 해결할 수있는 것 같습니다. (정렬 생략)// if you're going to the trouble to implement an entire Arena for memory, // making an arena_ptr won't be much work. basically the same as an auto_ptr, // except that it knows which arena to deallocate from when destructed. arena_ptr<char> string(a); string.allocate(80); // or: arena_ptr<char> string; string.allocate(a, 80); arena_ptr<int> intp(a); intp.allocate(); // or: arena_ptr<int> intp; intp.allocate(a); arena_ptr<foo> fp(a); fp.allocate(); // or: arena_ptr<foo>; fp.allocate(a); // use templates in 'arena.allocate(...)' to determine that foo has // a constructor which needs to be called. do something similar // for destructors in '~arena_ptr()'.
'과부하의 위험 :: Operator New []'에서 저자는
new(p) obj[10]
. 왜 대신 (훨씬 덜 모호한) :obj *p = (obj *)special_malloc(sizeof(obj[10])); for(int i = 0; i < 10; ++i, ++p) new(p) obj;
'C ++의 메모리 할당 디버깅'. 여기서 논쟁 할 수 없습니다.
전체 기사는 수업을 중심으로 진행되는 것 같습니다 중요한 생성자 그리고 소멸자 a 사용자 정의 메모리 관리 체계. 그것이 유용 할 수 있지만 논쟁 할 수는 없지만 공통점이 매우 제한적입니다.
기본적으로 우리는 신규 및 클래스 당 할당자를 배치했습니다. 이러한 접근법으로 어떤 문제를 해결할 수 없습니까?
또한, 나는 단지 두껍고 미친 경우에 당신의 이상적인 C ++, 대체 할 것 operator new
? 필요에 따라 구문을 발명하십시오 이상적인, 단순히 이러한 문제를 더 잘 이해하도록 도와줍니다.
해결책
글쎄, 이상적인 아마도 어떤 종류의 삭제를 삭제할 필요가 없을 것입니다. 쓰레기 수집 환경을 갖추고 프로그래머가 전체 문제를 피하도록하십시오.
rant의 불만은
- "나는 Malloc이하는 방식이 마음에 들었다"
- "나는 알려진 유형의 개체를 명시 적으로 만들어야하는 것을 좋아하지 않는다"
그는 당신이 둘 다 구현해야한다는 성가신 사실에 대해 옳습니다. new
그리고 new[]
, 그러나 당신은 C의 의미론의 핵심을 유지하려는 Stroustrups의 욕구에 의해 강요당했습니다. 배열에서 포인터를 말할 수 없으므로 컴파일러를 직접 알리야합니다. 당신은 그것을 고칠 수 있지만, 그렇게하는 것은 언어의 C 부분의 의미를 근본적으로 바꾸는 것을 의미합니다. 더 이상 정체성을 사용할 수 없습니다
*(a+i) == a[i]
모든 C 코드의 매우 큰 하위 집합이 깨질 것입니다.
그래서 당신은 언어를 가질 수 있습니다
배열에 대한보다 복잡한 개념을 구현하고 포인터 산술의 경이로움을 제거하여 마약 벡터 또는 유사한 것과 함께 배열을 구현합니다.
쓰레기가 수집되었으므로 자신의 것이 필요하지 않습니다.
delete
규율.
즉, Java를 다운로드 할 수 있습니다. 그런 다음 언어를 변경하여 확장 할 수 있습니다.
- 강하게 입력되지 않으므로 확인하십시오
void *
업 캐스트가 제거되고
... 그러나 그것은 컴파일러가 보지 않고 foo를 막대로 변환하는 코드를 작성할 수 있음을 의미합니다. 원한다면 Ducktyping도 가능합니다.
문제는 일단 그런 일을 한 후에는 C-ish 구문이있는 Python 또는 Ruby가 있다는 것입니다.
Stroustrup이 Cfront 1.0의 테이프를 보냈기 때문에 C ++를 작성했습니다. C ++와 관련된 많은 역사는 이제 C 세계에 맞을 수있는 OO 언어를 갖고 싶어하는 욕구에서 나옵니다. 에펠과 같이 같은시기에 나온 다른 더 만족스러운 언어가 많이있었습니다. C ++가 이겼던 것 같습니다. 나는 그것이 이겼다고 생각한다 왜냐하면 C 세계에 맞을 수 있습니다.
다른 팁
IMHO는 매우 오해의 소지가 있으며 저자가 더 미세한 세부 사항을 이해하는 것 같습니다. 단지 그가 오도하고 싶어하는 것 같습니다. IMHO, 인수의 결함을 보여주는 핵심 요점은 다음과 같습니다.
void* operator new(std::size_t size, void* ptr) throw();
표준은 위의 함수에 다음 속성이 있음을 정의합니다.
보고: ptr.
메모: 의도적으로 다른 행동을 수행하지 않습니다.
이 기능을 다시 만들기 위해 의도적으로 다른 행동을 수행하지 않습니다. 이것은 새로운 배치가하는 일의 열쇠이기 때문에 매우 중요합니다. 객체의 생성자를 호출하는 데 사용됩니다. 이것이 전부입니다. 명시 적으로 주목하십시오 크기 매개 변수는 언급되지 않았습니다.
시간이없는 사람들을 위해, 나의 요점을 요약하기 위해 : c에서 'malloc'가하는 모든 일은 ":: operator new"를 사용하여 C ++에서 수행 할 수 있습니다. 유일한 차이점은 집계 유형이없는 경우 (즉)입니다. 소멸자와 생성자가 호출되어야하는 유형을 호출 한 다음 해당 생성자와 소멸자를 호출해야합니다. 이러한 유형은 C에는 명시 적으로 존재하지 않으므로 "Malloc이 더 잘 수행"한다는 주장을 사용하는 것이 유효하지 않습니다. 'C'에 구조물이있는 경우 특별한 "초기화"함수가있는 경우 해당 "DestroyMe"로 호출되어야합니다. 저자가 만든 모든 점은 해당 구조물이 아닌 C ++ 구조물과 동일하게 적용됩니다.
그의 포인트 중 일부를 명시 적으로 가져옵니다.
여러 상속을 구현하려면 컴파일러는 실제로 일부 캐스트 중에 포인터 값을 변경해야합니다. 무효로 변환 할 때 결국 원하는 값을 알 수 없습니다. 따라서 일반적인 기능은 C ++에서 Malloc의 역할을 수행 할 수 없습니다.
이것은 다시 맞지 않습니다 :: 운영자 역할을 수행합니다 Malloc:
class A1 { };
class A2 { };
class B : public A1, public A2 { };
void foo () {
void * v = ::operator new (sizeof (B));
B * b = new (v) B(); // Placement new calls the constructor for B.
delete v;
v = ::operator new (sizeof(int));
int * i = reinterpret_cast <int*> (v);
delete v'
}
위에서 언급했듯이 B의 생성자를 호출하려면 새로 배치해야합니다. 'I'의 경우 캐스트 할 수 있습니다. 무효의* 에게 int* 문제가 없으면 다시 배치를 사용하면 유형 확인이 향상됩니다.
그가하는 또 다른 요점은 정렬 요구 사항에 관한 것입니다.
New Char [...]에 의해 반환 된 메모리가 반드시 구조물 intlist의 정렬 요구 사항을 충족하지는 않습니다.
3.7.3.1/2 미만의 표준은 다음과 같이 말합니다.
반환 된 포인터는 완전한 객체 유형의 포인터로 변환 된 다음 할당 된 스토리지의 객체 또는 배열에 액세스하는 데 사용되도록 적절하게 정렬되어야합니다 (저장소가 해당 거래 기능에 대한 호출에 의해 명시 적으로 처리 될 때까지) .
나에게 꽤 분명해 보인다.
전문화 된 할당 자 하에서 저자는 당신이 가질 수있는 잠재적 인 문제를 설명합니다. 할당자를 메모리 자체를 할당하는 유형에 대한 인수로 사용해야하며 구성된 물체는 파괴자에게 명시 적으로 호출해야합니다. 다시 말하지만, 이것은 할당 자 객체를 "initalizeme"으로 전달하는 것과 C 구조물을 호출하는 것과 어떻게 다른가요?
Destructor를 호출하는 것과 관련하여 C ++에서는 특별한 종류의 스마트 포인터를 쉽게 만들 수 있습니다. "Placement_Pointer"라고 부를 수 있습니다. "Placement_Pointer"라고 부르겠습니다. 결과적으로 우리는 다음을 가질 수 있습니다.
template <typename T>
class placement_pointer {
// ...
~placement_pointer() {
if (*count == 0) {
m_b->~T();
}
}
// ...
T * m_b;
};
void
f ()
{
arena a;
// ...
foo *fp = new (a) foo; // must be destroyed
// ...
fp->~foo ();
placement_pointer<foo> pfp = new (a) foo; // automatically !!destructed!!
// ...
}
내가 댓글을 달고 싶은 마지막 요점은 다음과 같습니다.
G ++는 다음과 같이 정의 된 "배치"연산자가 함께 제공됩니다.
inline void *
operator new[](size_t, void *place)
{
return place;
}
위에서 언급 한 바와 같이, 이런 식으로 구현 될뿐만 아니라 표준에 따라야합니다.
obj를 소멸자가있는 수업으로합시다. 어딘가에 크기가 크기 (OBJ [10]) 바이트의 어딘가에 있고 해당 위치에 유형 OBJ의 10 개 객체를 구성하고 싶다고 가정 해 봅시다. (C ++는 크기 (OBJ [10])을 10 * sizeof (OBJ)로 정의합니다.이 배치 연산자 New []로 그렇게 할 수 있습니까? 예를 들어 다음 코드는 그렇게하는 것 같습니다.
obj *
f ()
{
void *p = special_malloc (sizeof (obj[10]));
return new (p) obj[10]; // Serious trouble...
}
불행히도이 코드는 올바르지 않습니다. 일반적으로, size_t 인수가 연산자에게 전달 된 인수가 실제로 할당되는 배열의 크기에 해당한다는 보장은 없습니다.
그러나 그가 정의를 공급함으로써 강조 표시되면, 크기 인수는 할당 함수에 사용되지 않습니다. 할당 함수가 있습니다 아무것도 아님 - 위의 배치 표현식의 유일한 영향은 예상대로 10 개의 배열 요소의 생성자를 호출하는 것입니다.
이 코드에는 다른 코드가 있지만 저자가 나열된 코드는 아닙니다.