IT박스

C # 개발자를위한 C ++

itboxs 2021. 1. 9. 09:37
반응형

C # 개발자를위한 C ++


저는 .NET 개발자이며 그 전에 VB6에서 작업했습니다. 저는 이러한 환경에 매우 익숙해졌으며 가비지 수집 언어의 맥락에서 작업했습니다. 그러나 이제는 네이티브 C ++로 제 기술을 강화하고 싶고 제 자신이 약간 압도당했습니다. 아이러니하게도, 포인터와 메모리 관리에 대해 꽤 잘 이해하고 있다고 생각하기 때문에 초보자를위한 일반적인 걸림돌이라고 생각하는 것은 아닙니다. 저에게 약간 혼란스러운 점은 다음과 같은 내용입니다.

  • 다른 라이브러리 참조 / 사용
  • 다른 사람이 사용할 수 있도록 라이브러리 노출
  • 문자열 처리
  • 데이터 유형 변환
  • 좋은 프로젝트 구조
  • 사용할 데이터 구조 (예 : C #에서 List<T>많이 사용합니다. C ++에서 비슷하게 작동하는 것은 무엇입니까?)

사용하는 IDE에 따라 지침이 다르기 때문에 좀 더 보편적 인 것을 찾고있었습니다. 또는 최악의 경우 Microsoft의 컴파일러 / IDE를 사용하는 데 중점을 둡니다. 또한 명확하게 말하면 일반적인 프로그래밍 관행 (디자인 패턴, 코드 완성 등)에 대한 어떤 것도 찾고 있지 않습니다. 이러한 주제에 대해 잘 알고 있다고 생각합니다.


나는 당신이 포인터와 메모리 관리에 대해 잘 알고 있다고 말하는 것을 알고 있지만 여전히 중요한 트릭을 설명하고 싶습니다. 일반적으로 사용자 코드에 신규 / 삭제를 하지 마십시오 .

모든 리소스 획득 (동기화 잠금, 데이터베이스 연결 또는 메모리 청크 또는 획득 및 해제해야하는 기타 항목)은 생성자가 획득을 수행하고 소멸자가 리소스를 해제하도록 객체에 래핑되어야합니다. 이 기술은 RAII 로 알려져 있으며 기본적으로 메모리 누수를 방지 하는 방법입니다. 그것에 익숙해. C ++ 표준 라이브러리는 분명히 이것을 광범위하게 사용하므로 그것이 어떻게 작동하는지 느낄 수 있습니다. 질문의 동등한 조금 점프 List<T>IS std::vector<T>, 그리고 메모리를 관리 할 수 RAII를 사용합니다. 다음과 같이 사용합니다.

void foo() {

  // declare a vector *without* using new. We want it allocated on the stack, not
  // the heap. The vector can allocate data on the heap if and when it feels like
  // it internally. We just don't need to see it in our user code
  std::vector<int> v;
  v.push_back(4);
  v.push_back(42); // Add a few numbers to it

  // And that is all. When we leave the scope of this function, the destructors 
  // of all local variables, in this case our vector, are called - regardless of
  // *how* we leave the function. Even if an exception is thrown, v still goes 
  // out of scope, so its destructor is called, and it cleans up nicely. That's 
  // also why C++ doesn't have a finally clause for exception handling, but only 
  // try/catch. Anything that would otherwise go in the finally clause can be put
  // in the destructor of a local object.
} 

C ++ 프로그래머가 배워야하는 원칙 하나를 선택해야한다면 그것은 위의 것입니다. 범위 지정 규칙과 소멸자가 작동하도록하십시오. 안전한 코드를 작성하는 데 필요한 모든 보장을 제공합니다.

문자열 처리 :

std::string당신의 친구입니다. C에서는 char 배열 (또는 char 포인터)을 사용하지만 문자열처럼 작동하지 않기 때문에 불쾌합니다. C ++에는 예상대로 동작하는 std :: string 클래스가 있습니다. 유일하게 기억해야 할 점은 "hello world"가 std :: string이 아니라 char [12] 유형이라는 것입니다. (C 호환성을 위해) 따라서 원하는 동작을 얻으려면 문자열 리터럴 ( "hello world"와 같이 따옴표로 묶인 것)을 std :: string으로 명시 적으로 변환해야합니다. 여전히 작성할 수 있습니다.

std::string s = "hello world";

C 스타일 문자열 (예 : "hello world"와 같은 리터럴)은 암시 적으로 std :: string으로 변환 할 수 있지만 항상 작동하지는 않기 때문입니다. "hello"+ "world"는 컴파일되지 않을 것입니다. 두 개의 포인터에 대해 정의되지 않았습니다. 그러나 "hello worl"+ 'd' 컴파일되지만 현명한 작업을 수행하지는 않습니다. 문자열에 char를 추가하는 대신 char의 정수 값 (int로 승격 됨)을 가져 와서 포인터 값에 추가합니다.

std :: string ( "hello worl") + "d"는 예상대로 수행합니다. 왜냐하면 왼쪽은 이미 std :: string이고 더하기 연산자는 std :: string이 수행하도록 오버로드 되었기 때문입니다. 오른쪽이 char * 또는 단일 문자 인 경우에도 예상 할 수 있습니다.

문자열에 대한 마지막 참고 사항 : std :: string은 1 바이트 데이터 유형 인 char을 사용합니다. 즉, 유니 코드 텍스트에는 적합하지 않습니다. C ++는 플랫폼에 따라 2 바이트 또는 4 바이트 인 와이드 문자 유형 wchar_t를 제공하며 일반적으로 유니 코드 텍스트에 사용됩니다 (두 경우 모두 C ++ 표준이 실제로 문자 집합을 지정하지는 않음). 그리고 wchar_t의 문자열은 std :: wstring이라고합니다.

도서관 :

근본적으로 존재하지 않습니다. C ++ 언어에는 라이브러리 개념이 없으며 익숙해지는 데 약간의 시간이 걸립니다. 다른 파일 (일반적으로 확장자가 .h 또는 .hpp 인 헤더 파일)을 #include 할 수 있지만 이는 단순히 그대로 복사 / 붙여 넣기입니다. 전처리 기는 단순히 두 파일을 결합하여 변환 단위라고합니다. 여러 소스 파일에는 일반적으로 동일한 헤더가 포함되며 특정 상황에서만 작동하므로이 부분은 악명 높은 C ++ 컴파일 모델을 이해하는 데 중요합니다. C # 컴파일러처럼 여러 개의 개별 모듈을 컴파일하고 그 사이에 어떤 종류의 메타 데이터를 확장하는 대신 각 번역 단위가 개별적으로 컴파일됩니다.

물론 라이브러리를 작성하는 플랫폼 별 방법이 있습니다. Windows에서 .dll 또는 .libs를 만들 수 있지만 .lib는 응용 프로그램에 연결되어있는 반면 .dll은 .NET에서와 같이 앱과 함께 번들해야하는 별도의 파일입니다. Linux에서 동등한 파일 유형은 .so 및 .a이며 모든 경우에 사람들이 라이브러리에 대해 개발할 수 있도록 관련 헤더 파일도 제공해야합니다.

데이터 유형 변환 :

정확히 무엇을 찾고 있는지 잘 모르겠지만 중요한 점은 다음과 같은 "전통적인"캐스트가 나쁘다는 것입니다.

int i = (int)42.0f; 

이에 대한 몇 가지 이유가 있습니다. 첫째, 여러 가지 다른 유형의 캐스트를 순서대로 수행하려고 시도하며 컴파일러가 어떤 유형의 캐스트를 적용하는지에 놀랄 수 있습니다. 둘째, 검색에서 찾기가 어렵고, 셋째, 충분히 못 생겼습니다. 캐스트는 일반적으로 피하는 것이 가장 좋으며 C ++에서는이를 상기시키기 위해 약간보기 흉하게 만들어졌습니다. ;)

// The most common cast, when the types are known at compile-time. That is, if 
// inheritance isn't involved, this is generally the one to use
static_cast<U>(T); 

// The equivalent for polymorphic types. Does the same as above, but performs a 
// runtime typecheck to ensure that the cast is actually valid
dynamic_cast<U>(T); 

// Is mainly used for converting pointer types. Basically, it says "don't perform
// an actual conversion of the data (like from 42.0f to 42), but simply take the
// same bit pattern and reinterpret it as if it had been something else). It is
// usually not portable, and in fact, guarantees less than I just said.
reinterpret_cast<U>(T); 

// For adding or removing const-ness. You can't call a non-const member function
// of a const object, but with a const-cast you can remove the const-ness from 
// the object. Generally a bad idea, but can be necessary.
const_cast<U>(T);

알다시피, 이러한 캐스트는 훨씬 더 구체적입니다. 즉, 캐스트가 유효하지 않은 경우 컴파일러가 오류를 줄 수 있습니다 (전통적인 구문과 달리 작동하는 캐스트를 찾을 때까지 위의 캐스트 중 하나를 시도합니다). ), 크고 장황하여 검색 할 수 있으며 가능하면 피해야 함을 상기시킵니다. ;)

표준 라이브러리 :

Finally, getting back to data structures, put some effort into understanding the standard library. It is small, but amazingly versatile, and once you learn how to use it, you'll be in a far better position.

The standard library consists of several pretty distinct building blocks (the library has kind of accumulated over time. Parts of it were ported from C. The I/O streams library are adopted from one place, and the container classes and their associated functionality are adopted from a completely different library, and are designed noticeably different. The latter are part of what is often referred to as the STL (Standard Template Library). Strictly speaking, that is the name of the library that, slightly modified, got adopted into the C++ Standard Library.

The STL is key to understanding "modern C++". It is composed of three pillars, containers, iterators and algorithms. In a nutshell, containers expose iterators, and algorithms work on iterator pairs.

The following example takes a vector of int's, adds 1 to each element, and copies it to a linked list, just for the sake of example:

int add1(int i) { return i+1; } // The function we wish to apply

void foo() {
  std::vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
  v.push_back(4);
  v.push_back(5); // Add the numbers 1-5 to the vector

  std::list<int> l;

  // Transform is an algorithm which applies some transformation to every element
  // in an iterator range, and stores the output to a separate iterator
  std::transform ( 
  v.begin(),
  v.end(), // Get an iterator range spanning the entire vector
  // Create a special iterator which, when you move it forward, adds a new 
  // element to the container it points to. The output will be assigned to this
  std::back_inserter(l) 
  add1); // And finally, the function we wish to apply to each element
}

The above style takes some getting used to, but it is extremely powerful and concise. Because the transform function is templated, it can accept any types as input, as long as they behave as iterators. This means that the function can be used to combine any type of containers, or even streams or anything else that can be iterated through, as long as the iterator is designed to be compatible with the STL. We also don't have to use the begin/end pair. Instead of the end iterator, we could have passed one pointing to the third element, and the algorithm would then have stopped there. Or we could have written custom iterators which skipped every other elements, or whatever else we liked. The above is a basic example of each of the three pillars. We use a container to store our data, but the algorithm we use to process it doesn't actually have to know about the container. It just has to know about the iterator range on which it has to work. And of course each of these three pillars can be extended by writing new classes, which will then work smoothly together with the rest of the STL.

In a sense, this is very similar to LINQ, so since you're coming from .NET, you can probably see some analogies. The STL counterpart is a bit more flexible though, at the cost of slightly weirder syntax. :) (As mentioned in comments, it is also more efficient. In general, there is zero overhead to STL algorithms, they can be just as efficient as hand-coded loops. This is often surprising, but is possible because all relevant types are known at compile-time (which is a requirement for templates to work), and C++ compilers tend to inline aggressively.)


You've got some toolkits available. For example, there are STL (Standard Template Library) and Boost/TR1 (extensions to STL) that are considered industry standards (well, STL is, at least). These provide lists, maps, sets, shared pointers, strings, streams, and all sorts of other handy tools. Best of all, they're widely supported across compilers.

As for data conversions, you can either do casts or create explicit converter functions.

Libraries - You can either create static libraries (get absorbed into the final executable) or DLLs (you're familiar with these, already). MSDN is an awesome resource for DLLs. Static libraries depend on your build environment.

In general, this is my advice: - Get to know your IDE of choice very well - Purchase "C++ The Complete Reference" by Herbert Schildt, which I consider to be an excellent tome on all things C++ (includes STL)

Considering your background, you should be well set once you do both of those.


I'll not repeat what others have said about libraries and such, but if you're serious about C++, do yourself a favor and pick up Bjarne Stroustrup's "The C++ Programming Language."

It took me years of working in C++ to finally pick up a copy, and once I did, I spent an afternoon slapping my forehead saying "of course! I should have realized! etc."

(Ironically, I had EXACTLY the same experience with K&R's "The C Programming Language." Someday, I'll learn to just go get "The Book" on day 1.)


I wrote small cheat sheet for such programmers. You also might be interested with more complicated cases, such as varargs


Referencing and using other libraries, if you're including the source, is accomplished simply by #including the header files for the library into whatever .cpp file you need them in (and then compile the source for the library along with your project). Most of the time, however, you'll probably be using a .lib (static library) or .dll (dynamic library). Most (all?) DLLs come with a .lib file, so the procedure for both types is the same: include the appropriate header files where you need them, then add the associated .lib file during the linking step (in visual studio, I think you can just add the file to the project).

It's been a long time since I've created my own libraries for others to use, so I'll let someone else answer that part. Or I'll come back and edit this answer tomorrow, since I'm going to have to create a .lib for work tomorrow :)

String stuff is usually accomplished with std::string. Under special circumstances, you may also use the old C-style sprintf() function, but that's generally discouraged.

As far as the data structures you're looking for, check out the STL (Standard Template Library). It includes List, Vector, Map, String, etc that should be familiar to you. I'm not sure what you mean by type conversions... I assume you know about casting, so you must mean something more complex than that, in which case it's probably specific to the types you're trying to convert. Maybe someone else can offer more info.


In response to "Referencing/using other libraries"

Information regarding explicit loading of DLL's in C/C++ for both windows and linux include...

Windows:

Windows DLL Tutorial

Functions: LoadLibrary, GetProcAddress, FreeLibrary

Linux:

Functions: dlopen, dlsym, dlerror, dlclose

Linux DLL Tutorial

ReferenceURL : https://stackoverflow.com/questions/285723/c-for-a-c-sharp-developer

반응형