1. Preface 2. Use the Tools Available 3. Style 4. Considering Safety 5. Considering Maintainability 6. Considering Portability 7. Considering Threadability 8. Considering Performance 9. Enable Scripting 10. Further Reading 11. Final Thoughts Table of Contents C++ Best Practices 2 C++ Best Practices: A Forkable Coding Standards Document This document is meant to be a collaborative discussion of the best practices in C++. It complements books such as Effective C++ (Meyers) and C++ Coding Standards (Alexandrescu, Sutter). We fill in some of the lower level details that they don't discuss and provide specific stylistic recommendations while also discussing how to ensure overall code quality. In all cases brevity and succinctness is preferred. Examples are preferred for making the case for why one option is preferred over another. If necessary, words will be used. C++ Best Practices by Jason Turner is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License. Disclaimer This document is based on my personal experiences. You are not supposed to agree with it 100%. It exists as a book on GitHub so that you can fork it for your own uses or submit back proposed changes for everyone to share. Preface C++ Best Practices 3Preface An automated framework for executing these tools should be established very early in the development process. It should not take more than 2-3 commands to checkout the source code, build, and execute the tests. Once the tests are done executing, you should have an almost complete picture of the state and quality of the code. Source control is an absolute necessity for any software development project. If you are not using one yet, start using one. GitHub - allows for unlimited public repositories, must pay for a private repository. Bitbucket - allows for unlimited private repositories with up to 5 collaborators, for free. SourceForge - open source hosting only. GitLab, Subversion, BitKeeper, many many others... The above are the most popular free services. Visual Studio Online(http://www.visualstudio.com/what-is-visual-studio-online-vs) - allows for unlimited public repositories, must pay for private repository. Repositories can be git or TFVC. Additionally: Issue tracking, project planning (multiple Agile templates, such as SCRUM), integrated hosted builds, integration of all this into Microsoft Visual Studio. Windows only. Use an industry standard widely accepted build tool. This prevents you from reinventing the wheel whenever you discover / link to a new library / package your product / etc. Examples include: CMake Consider: https://github.com/sakra/cotire/ for build performance Consider: https://github.com/toeb/cmakepp for enhanced usability Biicode Waf FASTBuild Ninja - can greatly improve the incremental build time of your larger projects. Can be used as a target for CMake. Bazel - Note: MacOS and Linux only. gyp Google's build tool for chromium. Remember, it's not just a build tool, it's also a programming language. Try to maintain good clean build scripts and follow the recommended practices for the tool you are using. Once you have picked your build tool, set up a continuous integration environment. Continuous Integration (CI) tools automatically build the source code as changes are pushed to the repository. These can be hosted privately or with a CI host. Travis CI works well with C++ designed for use with GitHub free for public repositories on GitHub AppVeyor Use The Tools Available Source Control Build Tool Continuous Integration C++ Best Practices 4Use the Tools Available supports Windows, MSVC and MinGW free for public repositoris on GitHub Hudson CI / Jenkins CI Java Application Server is required supports Windows, OS X, and Linux extendable with a lot of plugins TeamCity has a free option for open source projects Decent CI simple ad-hoc continuous integration that posts results to GitHub supports Windows, OS X, and Linux used by ChaiScript Visual Studio Online(http://www.visualstudio.com/what-is-visual-studio-online-vs) Tightly integrated with the source repositories from Visual Studio Online Uses MSBuild (Visual Studio's build engine), which is available on Windows, OS X and Linux Provides hosted build agents and also allows for user-provided build agents Can be controlled and monitored from within Microsoft Visual Studio On-Premise installation via Microsoft Team Foundation Server If you have an open source, publicly-hosted project on GitHub: go enable travis-ci and AppVeyor integration right now. We'll wait for you to come back. For a simple example of how to enable it for your C++ CMake-based application, see here: https://github.com/ChaiScript/ChaiScript/blob/master/.travis.yml enable one of the coverage tools listed below (Codecov or Coveralls) enable Coverity Scan These tools are all free and relatively easy to set up. Once they are set up you are getting continuous building, testing, analysis and reporting of your project. For free. Use every available and reasonable set of warning options You should use as many compilers as you can for your platform(s). Each compiler implements the standard slightly differently and supporting multiple will help ensure the most portable, most reliable code. -Wall -Wextra -Wshadow -Wnon-virtual-dtor -pedantic -Wall -Wextra reasonable and standard -Wshadow warn the user if a variable declaration shadows one from a parent context -Wnon-virtual-dtor warn the user if a class with virtual functions has a non-virtual destructor. This helps catch hard to track down memory errors -Wold-style-cast warn for c-style casts -Wcast-align warn for potential performance problem casts -Wunused warn on anything being unused -Woverloaded-virtual warn if you overload (not override) a virtual function -pedantic -Weffc++ warning mode can be too noisy, but if it works for your project, use it also. Compilers GCC / Clang C++ Best Practices 5Use the Tools Available /W4 /W44640 /W4 - All reasonable warnings /w44640 - Enable warning on thread un-safe static member initialization Not recommended /Wall - Also warns on files included from the standard library, so it's not very useful and creates too many extra warnings. Start with very strict warning settings from the beginning. Trying to raise the warning level after the project is underway can be painful. Consider using the treat warnings as errors setting. /Wx with MSVC, -Werror with GCC / Clang include-what-you-use, example results clang-modernize, example results clang-check clang-tidy The best bet is the static analyzer that you can run as part of your automated build system. Cppcheck and clang meet that requirement for free options. Coverity has a free (for open source) static analysis toolkit that can work on every commit in integration with Travis CI and AppVeyor. Cppcheck is free and open source. It strives for 0 false positives and does a good job at it. Therefore all warnings should be enabled: -enable=all Clang's analyzer's default options are good for the respective platform. It can be used directly from CMake. They can also be called via clang-check and clang-tidy from the LLVM-based Tools. Can be enabled with the /analyze command line option. For now we will stick with the default options. MSVC General LLVM-based tools Static Analyzers Coverity Scan Cppcheck Clang's Static Analyzer MSVC's Static Analyzer ReSharper C++ / CLion C++ Best Practices 6Use the Tools Available Both of these tools from JetBrains offer some level of static analysis and automated fixes for common things that can be done better. They have options available for free licenses for open source project leaders. Qt Creator can plug into the clang static analyzer, but only on the commercial version of Qt Creator. http://metrixplusplus.sourceforge.net/ While not necessarily a static analyzer, Metrix++ can identify and report on the most complex sections of your code. Reducing complex code helps you and the compiler understand it better and optimize it better. A coverage analysis tool shall be run when tests are executed to make sure the entire application is being tested. Unfortunately, coverage analysis requires that compiler optimizations be disabled. This can result in significantly longer test execution times. Codecov integrates with Travis CI and Appveyor free for open source projects Coveralls integrates with Travis CI and Appveyor free for open source projects LCOV very configurable Gcovr Runtime code analyzer that can detect memory leaks, race conditions, and other associated problems. It is supported on various Unix platforms. Similar to valgrind. http://www.drmemory.org These tools provide many of the same features as Valgrind, but built into the compiler. They are easy to use and provide a report of what went wrong. AddressSanitizer ThreadSanitizer UndefinedBehaviorSanitizer Qt Creator Metrix++ Runtime Checkers Code Coverage Analysis Valgrind Dr Memory GCC / Clang Sanitizers Fuzzy Analyzers C++ Best Practices 7Use the Tools Available If you project accepts user defined input, considering running a fuzzy input tester. Both of these tools use coverage reporting to find new code executation paths and try to breed novel inputs for your code. They can find crashes, hangs, and inputs you didn't know were considered valid. american fuzzy lop LibFuzzer If it is determined by team consensus that the compiler or analyzer is warning on something that is either incorrect or unavoidable, the team will disable the specific error to as localized part of the code as possible. CMake, mentioned above, has a built in framework for executing tests. Make sure whatever build system you use has a way to execute tests built in. To further aid in executing tests, consider a library such as Google Test, Catch or Boost.Test to help you organize the tests. Unit tests are for small chunks of code, individual functions which can be tested standalone. There should be a test enabled for every feature or bug fix that is committed. See also Code Coverage Analysis. These are tests that are higher level than unit tests. They should still be limited in scope to individual features. Don't forget to make sure that your error handling is being tested and works properly as well. This will become obvious if you aim for 100% code coverage. Ignoring Warnings Testing Unit Tests Integration Tests Negative Testing C++ Best Practices 8Use the Tools Available Consistency of style is more important. Second most importance is following a style that the average C++ programmer is used to reading. C++ allows for arbitrary-length identifier names, so there's no reason to be terse when naming variables. Use descriptive names, and be consistent in the style. CamelCase snake_case are common examples. snake_case has the advantage that it can also work with spell checkers, if desired. Types start with capitals: MyClass functions and variables start with lower case: myMethod constants are all capital: const double PI=3.14159265358979323; C++ Standard Library (and other well-known C++ libraries like Boost) use these guidelines: Macro names use uppercase with underscores: INT_MAX Template parameter names use camel case: InputIterator All other names use snake case: unordered_map Name private data with a m_ prefix to distinguish it from public data. m_ stands for "member" data Name function parameters with an t_ prefix. t_ can be thought of as "the", but the meaning is arbitrary. The point is to distinguish function parameters from other variables in scope while giving us a consistent naming strategy. By using t_ for parameters and m_ for module data, we can have consistency with both public members of structs and private members of classes. Any prefix or postfix can be chosen for your organization. This is just one example. This suggestion is controversial, for a discussion about it see issue #11. struct Size { int width; int height; ValueType(int t_width, int t_height) : width(t_width), height(t_height) {} }; // this version might make sense for thread safety or something, // but more to the point, sometimes we need to hide data, sometimes we don't class PrivateSize { public: Style Common C++ Naming Conventions Distinguish Private Object Data Distinguish Function Parameters C++ Best Practices 9Style int width() const { return m_width; } int height() const { return m_height; } ValueType(int t_width, int t_height) : m_width(t_width), m_height(t_height) {} private: int m_width; int m_height; }; If you do, you risk broaching on names reserved for implementation use: http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier class MyClass { public: MyClass(int t_data) : m_data(t_data) { } int getData() const { return m_data; } private: int m_data; }; Make sure generated files go into build folder, not the source folder C++11 introduces nullptr which is a special type denoting a null pointer value. This should be used instead of 0 or NULL to indicate a null pointer. Comment blocks should use // , not /* */ . Using // makes it much easier to comment out a block of code while debugging. // this function does something int myFunc() { } To comment out this function block during debugging we might do: Don't Name Anything Starting With _ Well-Formed Example Enable Out-of-Source-Directory Builds Use nullptr Comments C++ Best Practices 10Style /* // this function does something int myFunc() { } */ which would be impossible if the function comment header used /* */ This causes the name space you are using to be pulled into the namespace of the header file. It litters the namespace and it may lead to name collisions in the future. Writing using namespace in an implementation file is fine though. Header files must contain a distinctly-named include guard to avoid problems with including the same header multiple times and to prevent conflicts with headers from other projects. #ifndef MYPROJECT_MYCLASS_HPP #define MYPROJECT_MYCLASS_HPP namespace MyProject { class MyClass { }; } #endif You may also consider using the #pragma once directive instead which is quasi-standard across many compilers. It's short and makes the intent clear. Leaving them off can lead to semantic errors in the code. // Bad Idea // this compiles and does what you want, but can lead to confusing // errors if close attention is not paid. for (int i = 0; i < 15; ++i) std::cout << i << std::endl; // Bad Idea // the cout is not part of the loop in this case even though it appears to be int sum = 0; for (int i = 0; i < 15; ++i) ++sum; std::cout << i << std::endl; // Good Idea // It's clear which statements are part of the loop (or if block, or whatever) int sum = 0; for (int i = 0; i < 15; ++i) { ++sum; std::cout << i << std::endl; } Never Use using namespace in a Header File Include Guards {} Are Required for Blocks. C++ Best Practices 11Style // Bad Idea // hard to follow if (x && y && myFunctionThatReturnsBool() && caseNumber3 && (15 > 12 || 2 < 3)) { } // Good Idea // Logical grouping, easier to read if (x && y && myFunctionThatReturnsBool() && caseNumber3 && (15 > 12 || 2 < 3)) { } Many projects and coding standards have a soft guideline that one should try to use less than about 80 or 100 characters per line. Such code is generally easier to read. It also makes it possible to have two separate files next to each other on one screen without having a tiny font. ... <> is reserved for system includes. // Bad Idea. Requires extra -I directives to the compiler // and goes against standards #include #include // Worse Idea // requires potentially even more specific -I directives and // makes code more difficult to package and distribute #include #include // Good Idea // requires no extra params and notifies the user that the file // is a local file #include #include "MyHeader.hpp" ...with the member initializer list // Bad Idea class MyClass { public: MyClass(int t_value) { m_value = t_value; } private: int m_value; }; // Good Idea // C++'s member initializer list is unique to the language and leads to // cleaner code and potential performance gains that other languages cannot // match Keep Lines a Reasonable Length Use "" for Including Local Files Initialize Member Variables C++ Best Practices 12Style class MyClass { public: MyClass(int t_value) : m_value(t_value) { } private: int m_value; }; In C++11 you may consider always giving each member a default value, e.g. by writing // ... // private: int m_value = 0; // ... // inside the class body. This makes sure that no constructor ever "forgets" to initialize a member object. Forgetting to initialize a member is a source of undefined behavior bugs which are often extremely hard to find. There is almost never a reason to declare an identifier in the global namespaces. Instead, functions and classes should exist in an appropriately named namespace or in a class inside of a namespace. Identifiers which are placed in the global namespace risk conflicting with identifiers from other libraries (mostly C, which doesn't have namespaces). The standard library generally returns size_t for anything related to size. What exactly size_t is is implementation defined. In general, using auto will avoid most of these issues, but not all. Make sure you stick with the correct integer types and remain consistent with the C++ stdlib. It might not warn on the platform you are currently using, but it probably will when you change platforms. Ultimately this is a matter of preference, but .hpp and .cpp are widely recognized by various editors and tools. So the choice is pragmatic. Specifically, Visual Studio only automatically recognizes .cpp and .cxx for C++ files, and Vim doesn't necessarily recognize .cc as a C++ file. One particularly large project (OpenStudio) uses .hpp and .cpp for user-generated files and .hxx and .cxx for tool-generated files. Both are well recognized and having the distinction is helpful. Some editors like to indent with a mixture of tabs and spaces by default. This makes the code unreadable to anyone not using the exact same tab indentation settings. Always Use Namespaces Use the Correct Integer Type For stdlib Features Use .hpp and .cpp for Your File Extensions Never Mix Tabs and Spaces C++ Best Practices 13Style assert(registerSomeThing()); // make sure that registerSomeThing() returns true The above code succeeds when making a debug build, but gets removed by the compiler when making a release build, giving you different behavior between debug and release builds. This is because assert() is a macro which expands to nothing in release mode. They can help you stick to DRY principles. They should be preferred to macros, because macros do not honor namespaces, etc. The Rule of Zero states that you do not provide any of the functions that the compiler can provide (copy constructor, assignment operator, move constructor, destructor, move constructor) unless the class you are constructing does some novel form of ownership. The goal is to let the compiler provide optimal versions that are automatically maintained when more member variables are added. The original article provides the background, while a follow up article explains techniques for implementing nearly 100% of the time. Never Put Code with Side Effects Inside an assert() Don't Be Afraid of Templates Use Operator Overloads Judiciously Consider the Rule of Zero C++ Best Practices 14Style const tells the compiler that a variable or method is immutable. This helps the compiler optimize the code and helps the developer know if a function has a side effect. Also, using const & prevents the compiler from copying data unnecessarily. Here are some comments on const from John Carmack. // Bad Idea class MyClass { public: MyClass(std::string t_value) : m_value(t_value) { } std::string get_value() { return m_value; } private: std::string m_value; } // Good Idea class MyClass { public: MyClass(const std::string &t_value) : m_value(t_value) { } std::string get_value() const { return m_value; } private: std::string m_value; } You don't want to have to pay a cost for copying the data when you don't need to, but you also don't want to have to safely return data in a threading scenario. See also this discussion for more information: https://github.com/lefticus/cppbestpractices/issues/21 Raw memory access, allocation and deallocation, are difficult to get correct in C++ without risking memory errors and leaks. C++11 provides tools to avoid these problems. // Bad Idea MyClass *myobj = new MyClass; Considering Safety Const as Much as Possible Consider Return By Value for Mutable Data, const & for Immutable Avoid Raw Memory Access C++ Best Practices 15Considering Safety // ... delete myobj; // Good Idea auto myobj = std::make_unique(constructor_param1, constructor_param2); // C++14 auto myobj = std::unique_ptr(new MyClass(constructor_param1, constructor_param2)); // C++11 auto mybuffer = std::make_unique(length); // C++14 auto mybuffer = std::unique_ptr(new char[length]); // C++11 // or for reference counted objects auto myobj = std::make_shared(); // ... // myobj is automatically freed for you whenever it is no longer used. Both of these guarantee contiguous memory layout of objects and can (and should) completely replace your usage of C- style arrays for many of the reasons listed for not using bare pointers. Also, avoid using std::shared_ptr to hold an array. Exceptions cannot be ignored. Return values, such as using boost::optional , can be ignored and if not checked can cause crashes or memory errors. An exception, on the other hand, can be caught and handled. Potentially all the way up the highest level of the application with a log and automatic restart of the application. Stroustrup, the original designer of C++, makes this point much better than I ever could. Use the C++-style cast(static_cast<>, dynamic_cast<> ...) instead of the C-style cast. The C++-style cast allows more compiler checks and is considerable safer. // Bad Idea double x = getX(); int i = (int) x; // Good Idea int i = static_cast(x); Additionaly the C++ cast style is more visible and has the possiblity to search for. How to Prevent The Next Heartbleed by David Wheeler is a good analysis of the current state of code safety and how to ensure safe code. Use std::array or std::vector Instead of C-style Arrays Use Exceptions Use C++-style cast instead of C-style cast Additional Resources C++ Best Practices 16Considering Safety Compiler definitions and macros are replaced by the preprocessor before the compiler is ever run. This can make debugging very difficult because the debugger doesn't know where the source came from. // Bad Idea #define PI 3.14159; // Good Idea namespace my_project { class Constants { public: // if the above macro would be expanded, then the following line would be: // static const double 3.14159 = 3.14159; // which leads to a compile-time error. Sometimes such errors are hard to understand. static const double PI = 3.14159; } } Know and understand the existing C++ standard algorithms and put them to use. See C++ Seasoning for more details. Considering Maintainability Avoid Compiler Macros Avoid Raw Loops C++ Best Practices 17Considering Maintainability Most portability issues that generate warnings are because we are not careful about our types. Standard library and arrays are indexed with size_t . Standard container sizes are reported in size_t . If you get the handling of size_t wrong, you can create subtle lurking 64-bit issues that arise only after you start to overflow the indexing of 32-bit integers. char vs unsigned char. http://www.viva64.com/en/a/0010/ Considering Portability Know Your Types C++ Best Practices 18Considering Portability This includes statics and singletons. Global data leads to unintended side effects between functions and can make code difficult or impossible to parallelize. Even if the code is not intended today for parallelization, there is no reason to make it impossible for the future. Much slower in threaded environments. In many or maybe even most cases, copying data is faster. Plus with move operations and such and things. For member variables it is good practice to use mutex and mutable together. This applies in both ways: A mutable member variable is presumed to be a shared variable so it should be synchronized with a mutex (or made atomic) If a member variable is itself a mutex, it should be mutable. This is required to use it inside a const member function. For more information see the following article from Herb Sutter: http://herbsutter.com/2013/05/24/gotw-6a-const- correctness-part-1-3/ See also related safety discussion about const & return values Considering Threadability Avoid Global Data Avoid Heap Operations Mutex and mutable go together (M&M rule, C++11) C++ Best Practices 19Considering Threadability This: // some header file class MyClass; void doSomething(const MyClass &); instead of: // some header file #include "MyClass.hpp" void doSomething(const MyClass &); This applies to templates as well: template class MyTemplatedType; This is a proactive approach to reduce compilation time and rebuilding dependencies. Templates are not free to instantiate. Instantiating many templates, or templates with more code than necessary increases compiled code size and build time. For more examples see this article. The tool Templight can be used to analyze the build time of your project. It takes some effort to get built, but once you do, it's a drop in replacement for clang++. After you build using Templight, you will need to analyze the results. The templight-tools project provides various methods. (Author's Note: I suggest using the callgrind converter and visualizing the results with kcachegrind). The compiler has to do something with each include directive it sees. Even if it stops as soon as it seems the #ifndef include guard, it still had to open the file and begin processing it. include-what-you-use is a tool that can help you identify which headers you need. Considering Performance Build Time Forward Declare When Possible Avoid Unnecessary Template Instantiations Analyze the Build Firewall Frequently Changing Header Files Don't Unnecessarily Include Headers C++ Best Practices 20Considering Performance This is a general form of "Firewall Frequently Changing Header Files" and "Don't Unnecessarily Include Headers." Tools like BOOST_PP can be very helpful, but they also put a huge burden on the preprocessor. These are not meant to supersede good design ccache warp, Facebook's preprocessor See this YouTube video for more details. If on Linux, consider using the gold linker for GCC. There's no real way to know where your bottlenecks are without analyzing the code. http://developer.amd.com/tools-and-sdks/opencl-zone/codexl/ http://www.codersnotes.com/sleepy The cleaner, simpler, and easier to read the code is, the better chance the compiler has at implementing it well. // This std::vector mos{mo1, mo2}; // -or- auto mos = std::vector{mo1, mo2}; // Don't do this std::vector mos; mos.push_back(mo1); mos.push_back(mo2); Initializer lists are significantly more efficient; reducing object copies and resizing of containers. Reduce the load on the preprocessor Consider using precompiled headers Consider Using Tools Put tmp on Ramdisk Use the gold linker Runtime Analyze the Code! Simplify the Code Use Initializer Lists Reduce Temporary Objects C++ Best Practices 21Considering Performance // Instead of auto mo1 = getSomeModelObject(); auto mo2 = getAnotherModelObject(); doSomething(mo1, mo2); // consider: doSomething(getSomeModelObject(), getAnotherModelObject()); This sort of code prevents the compiler from performing a move operation... Move operations are one of the most touted features of C++11. They allow the compiler to avoid extra copies by moving temporary objects instead of copying them in certain cases. Certain coding choices we make (such as declaring our own destructor or assignment operator or copy constructor) prevents the compiler from generating a move constructor. For most code, a simple ModelObject(ModelObject &&) = default; would suffice. However, MSVC2013 doesn't seem to like this code yet. shared_ptr objects are much more expensive to copy than you'd think they would be. This is because the reference count must be atomic and thread-safe. So this comment just re-enforces the note above: avoid temporaries and too many copies of objects. Just because we are using a pImpl it does not mean our copies are free. For more simple cases, the ternary operator can be used: // Bad Idea std::string somevalue; if (caseA) { somevalue = "Value A"; } else { somevalue = "Value B"; } // Better Idea const std::string somevalue = caseA?"Value A":"Value B"; More complex cases can be facilitated with an immediately-invoked lambda. // Bad Idea std::string somevalue; Enable move operations Kill shared_ptr Copies Reduce Copies and Reassignments as Much as Possible C++ Best Practices 22Considering Performance if (caseA) { somevalue = "Value A"; } else if(caseB) { somevalue = "Value B"; } else { somevalue = "Value C"; } // Better Idea const std::string somevalue = [&](){ if (caseA) { return "Value A"; } else if (caseB) { return "Value B"; } else { return "Value C"; } }(); Exceptions which are thrown and captured internally during normal processing slow down the application execution. They also destroy the user experience from within a debugger, as debuggers monitor and report on each exception event. It is best to just avoid internal exception processing when possible. We already know that we should not be using raw memory access, so we are using unique_ptr and shared_ptr instead, right? Heap allocations are much more expensive than stack allocations, but sometimes we have to use them. To make matters worse, creating a shared_ptr actually requires 2 heap allocations. However, the make_shared function reduces this down to just one. std::shared_ptr(new ModelObject_Impl()); // should become std::make_shared(); // (it's also more readable and concise) If possible use unique_ptr instead of shared_ptr . The unique_ptr does not need to keep track of its copies because it is not copyable. Because of this it is more efficient than the shared_ptr . Equivalent to shared_ptr and make_shared you should use make_unique (C++14 or greater) to create the unique_ptr : std::make_unique(); Current best practices suggest returning a unique_ptr from factory functions as well, then converting the unique_ptr to a shared_ptr if necessary. std::unique_ptr factory(); auto shared = std::shared_ptr(factory()); Avoid Excess Exceptions Get rid of “new” Prefer unique_ptr to shared_ptr C++ Best Practices 23Considering Performance std::endl implies a flush operation. It's equivalent to "\n" << std::flush . Variables should be declared as late as possible, and ideally only when it's possible to initialize the object. Reduced variable scope results in less memory being used, more efficient code in general, and helps the compiler optimize the code further. // Good Idea for (int i = 0; i < 15; ++i) { MyObject obj(i); // do something with obj } // Bad Idea MyObject obj; // meaningless object initialization for (int i = 0; i < 15; ++i) { obj = MyObject(i); // unnecessary assignment operation // do something with obj } // obj is still taking up memory for no reason Operations on double s are typically faster than float s. However, in vectorized operations, float might win out. Analyze the code and find out which is faster for your application! ... when it is semantically correct. Pre-increment is faster than post-increment because it does not require a copy of the object to be made. // Bad Idea for (int i = 0; i < 15; i++) { std::cout << i << '\n'; } // Good Idea for (int i = 0; i < 15; ++i) { std::cout << i << '\n'; } Even if many modern compilers will optimize these two loops to the same assembly code, it is still good practice to prefer ++i . There is absolutely no reason not to and you can never be certain that your code will not pass a compiler that does not optimize this. You should be also aware that the compiler will not be able optimize this only for integer types and not necessarily for all iterator or other user defined types. The bottom line is that it is always easier and recommended to use the pre-increment operator if it is semantically identical to the post-increment operator. Get rid of std::endl Limit Variable Scope Prefer double to float Prefer ++i to i++ Char is a char, string is a string C++ Best Practices 24Considering Performance // Bad Idea std::cout << someThing() << "\n"; // Good Idea std::cout << someThing() << '\n'; This is very minor, but a "\n" has to be parsed by the compiler as a const char * which has to do a range check for \0 when writing it to the stream (or appending to a string). A '\n' is known to be a single character and avoids many CPU instructions. If used inefficiently very many times it might have an impact on your performance, but more importantly thinking about these two usage cases gets you thinking more about what the compiler and runtime has to do to execute your code. C++ Best Practices 25Considering Performance The combination of scripting and compiled languages is very powerful. It gives us the things we've come to love about compiled languages: type safety, performance, thread safety options, consistent memory model while also giving us the flexibility to try something new quickly without a full rebuild. The VM based compiled languages have learned this already: JRuby, Jython, IronRuby, IronPython ChaiScript AngelScript luabind SWIG Enable Scripting C++ Best Practices 26Enable Scripting http://geosoft.no/development/cppstyle.html http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml (Note that Google's standard document makes several recommendations which we will NOT be following. For example, they explicitly forbid the use of exceptions, which makes RAII impossible.) https://isocpp.org/faq/ http://www.cplusplus.com/ http://www.gamasutra.com/view/news/128836/InDepth_Static_Code_Analysis.php - Article from John Carmack on the advantages of static analysis https://svn.boost.org/trac/boost/wiki/BestPracticeHandbook - Best Practice Handbook from Nial Douglas http://sourceforge.net/apps/mediawiki/cppcheck/index.php?title=ListOfChecks http://emptycrate.com/ Further Reading C++ Best Practices 27Further Reading Expand your horizons and use other programming languages. Other languages have different constructs and expressions. Learning what else is out there will encourage you to be more creative with your C++ and write cleaner, more expressive code. Final Thoughts C++ Best Practices 28Final Thoughts




需要 3 金币 [ 分享pdf获得金币 ] 0 人已下载





下载需要 3 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!