Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Discover millions of ebooks, audiobooks, and so much more with a free trial

Only $11.99/month after trial. Cancel anytime.

Professional C++
Professional C++
Professional C++
Ebook2,118 pages24 hours

Professional C++

Rating: 2 out of 5 stars

2/5

()

Read preview

About this ebook

Get up to date quickly on the new changes coming with C++17

Professional C++ is the advanced manual for C++ programming. Designed to help experienced developers get more out of the latest release, this book skims over the basics and dives right in to exploiting the full capabilities of C++17. Each feature is explained by example, each including actual code snippets that you can plug into your own applications. Case studies include extensive, working code that has been tested on Windows and Linux, and the author's expert tips, tricks, and workarounds can dramatically enhance your workflow. Even many experienced developers have never fully explored the boundaries of the language's capabilities; this book reveals the advanced features you never knew about, and drills down to show you how to turn these features into real-world solutions.

The C++17 release includes changes that impact the way you work with C++; this new fourth edition covers them all, including nested namespaces, structured bindings, string_view, template argument deduction for constructors, parallel algorithms, generalized sum algorithms, Boyer-Moore string searching, string conversion primitives, a filesystem API, clamping values, optional values, the variant type, the any type, and more. Clear explanations and professional-level depth make this book an invaluable resource for any professional needing to get up to date quickly.

  • Maximize C++ capabilities with effective design solutions
  • Master little-known elements and learn what to avoid
  • Adopt new workarounds and testing/debugging best practices
  • Utilize real-world program segments in your own applications

C++ is notoriously complex, and whether you use it for gaming or business, maximizing its functionality means keeping up to date with the latest changes. Whether these changes enhance your work or make it harder depends on how well-versed you are in the newest C++ features. Professional C++ gets you up to date quickly, and provides the answers you need for everyday solutions.

LanguageEnglish
PublisherWiley
Release dateMar 9, 2018
ISBN9781119421221
Professional C++

Related to Professional C++

Related ebooks

Programming For You

View More

Related articles

Reviews for Professional C++

Rating: 2.1666667 out of 5 stars
2/5

3 ratings0 reviews

What did you think?

Tap to rate

Review must be at least 10 words

    Book preview

    Professional C++ - Marc Gregoire

    INTRODUCTION

    For many years, C++ has served as the de facto language for writing fast, powerful, and enterprise-class object-oriented programs. As popular as C++ has become, the language is surprisingly difficult to grasp in full. There are simple, but powerful, techniques that professional C++ programmers use that don’t show up in traditional texts, and there are useful parts of C++ that remain a mystery even to experienced C++ programmers.

    Too often, programming books focus on the syntax of the language instead of its real-world use. The typical C++ text introduces a major part of the language in each chapter, explaining the syntax and providing an example. Professional C++ does not follow this pattern. Instead of giving you just the nuts and bolts of the language with little practical context, this book will teach you how to use C++ in the real world. It will show you the little-known features that will make your life easier, and the programming techniques that separate novices from professional programmers.

    WHO THIS BOOK IS FOR

    Even if you have used the language for years, you might still be unfamiliar with the more-advanced features of C++, or you might not be using the full capabilities of the language. Perhaps you write competent C++ code, but would like to learn more about design and good programming style in C++. Or maybe you’re relatively new to C++, but want to learn the right way to program from the start. This book will meet those needs and bring your C++ skills to the professional level.

    Because this book focuses on advancing from basic or intermediate knowledge of C++ to becoming a professional C++ programmer, it assumes that you have some knowledge of the language. Chapter 1 covers the basics of C++ as a refresher, but it is not a substitute for actual training and use of the language. If you are just starting with C++, but you have significant experience in another programming language such as C, Java, or C#, you should be able to pick up most of what you need from Chapter 1.

    In any case, you should have a solid foundation in programming fundamentals. You should know about loops, functions, and variables. You should know how to structure a program, and you should be familiar with fundamental techniques such as recursion. You should have some knowledge of common data structures such as queues, and useful algorithms such as sorting and searching. You don’t need to know about object-oriented programming just yet—that is covered in Chapter 5.

    You will also need to be familiar with the compiler you will be using to develop your code. Two compilers, Microsoft Visual C++ and GCC, are introduced later in this introduction. For other compilers, refer to the documentation that came with your compiler.

    WHAT THIS BOOK COVERS

    Professional C++ uses an approach to C++ programming that will both increase the quality of your code and improve your programming efficiency. You will find discussions on new C++17 features throughout this fourth edition. These features are not just isolated to a few chapters or sections; instead, examples have been updated to use new features when appropriate.

    Professional C++ teaches you more than just the syntax and language features of C++. It also emphasizes programming methodologies, reusable design patterns, and good programming style. The Professional C++ methodology incorporates the entire software development process, from designing and writing code, to debugging, and working in groups. This approach will enable you to master the C++ language and its idiosyncrasies, as well as take advantage of its powerful capabilities for large-scale software development.

    Imagine users who have learned all of the syntax of C++ without seeing a single example of its use. They know just enough to be dangerous! Without examples, they might assume that all code should go in the main() function of the program, or that all variables should be global—practices that are generally not considered hallmarks of good programming.

    Professional C++ programmers understand the correct way to use the language, in addition to the syntax. They recognize the importance of good design, the theories of object-oriented programming, and the best ways to use existing libraries. They have also developed an arsenal of useful code and reusable ideas.

    By reading and understanding this book, you will become a professional C++ programmer. You will expand your knowledge of C++ to cover lesser-known and often misunderstood language features. You will gain an appreciation for object-oriented design, and acquire top-notch debugging skills. Perhaps most important, you will finish this book armed with a wealth of reusable ideas that you can actually apply to your daily work.

    There are many good reasons to make the effort to be a professional C++ programmer, as opposed to a programmer who knows C++. Understanding the true workings of the language will improve the quality of your code. Learning about different programming methodologies and processes will help you to work better with your team. Discovering reusable libraries and common design patterns will improve your daily efficiency and help you stop reinventing the wheel. All of these lessons will make you a better programmer and a more valuable employee. While this book can’t guarantee you a promotion, it certainly won’t hurt.

    HOW THIS BOOK IS STRUCTURED

    This book is made up of five parts.

    Part I, Introduction to Professional C++, begins with a crash course in C++ basics to ensure a foundation of C++ knowledge. Following the crash course, Part I goes deeper into working with strings and string views because strings are used extensively in most examples throughout the book. The last chapter of Part I explores how to write readable C++ code.

    Part II, Professional C++ Software Design, discusses C++ design methodologies. You will read about the importance of design, the object-oriented methodology, and the importance of code reuse.

    Part III, C++ Coding the Professional Way, provides a technical tour of C++ from the professional point of view. You will read about the best ways to manage memory in C++, how to create reusable classes, and how to leverage important language features such as inheritance. You will also learn about the unusual and quirky parts of the language, techniques for input and output, error handling, string localization, and how to work with regular expressions. You will read about how to implement operator overloading, and how to write templates. This part also explains the C++ Standard Library, including containers, iterators, and algorithms. You will also read about some additional libraries that are available in the standard, such as the libraries to work with time, random numbers, and the filesystem.

    Part IV, Mastering Advanced Features of C++, demonstrates how you can get the most out of C++. This part of the book exposes the mysteries of C++ and describes how to use some of its more-advanced features. You will read about how to customize and extend the C++ Standard Library to your needs, advanced details on template programming, including template metaprogramming, and how to use multithreading to take advantage of multiprocessor and multicore systems.

    Part V, C++ Software Engineering, focuses on writing enterprise-quality software. You’ll read about the engineering practices being used by programming organizations today; how to write efficient C++ code; software testing concepts, such as unit testing and regression testing; techniques used to debug C++ programs; how to incorporate design techniques, frameworks, and conceptual object-oriented design patterns into your own code; and solutions for cross-language and cross-platform code.

    The book concludes with a useful chapter-by-chapter guide to succeeding in a C++ technical interview, an annotated bibliography, a summary of the C++ header files available in the standard, and a brief introduction to the Unified Modeling Language (UML).

    This book is not a reference of every single class, method, and function available in C++. The book C++ Standard Library Quick Reference by Peter Van Weert and Marc Gregoire¹ is a condensed reference to all essential data structures, algorithms, and functions provided by the C++ Standard Library. Appendix B lists a couple more references. Two excellent online references are:

    www.cppreference.com

    You can use this reference online, or download an offline version for use when you are not connected to the Internet.

    www.cplusplus.com/reference/

    When I refer to a Standard Library Reference in this book, I am referring to one of these detailed C++ references.

    WHAT YOU NEED TO USE THIS BOOK

    All you need to use this book is a computer with a C++ compiler. This book focuses only on parts of C++ that have been standardized, and not on vendor-specific compiler extensions.

    Note that this book includes new features introduced with the C++17 standard. At the time of this writing, some compilers are not yet fully C++17 compliant.

    You can use whichever C++ compiler you like. If you don’t have a C++ compiler yet, you can download one for free. There are a lot of choices. For example, for Windows, you can download Microsoft Visual Studio 2017 Community Edition, which is free and includes Visual C++. For Linux, you can use GCC or Clang, which are also free.

    The following two sections briefly explain how to use Visual C++ and GCC. Refer to the documentation that came with your compiler for more details.

    Microsoft Visual C++

    First, you need to create a project. Start Visual C++ and click File ➪ New ➪ Project. In the project template tree on the left, select Visual C++ ➪ Win32 (or Windows Desktop). Then select the Win32 Console Application (or Windows Console Application) template in the list in the middle of the window. At the bottom, specify a name for the project and a location where to save it, and click OK. A wizard opens². In this wizard, click Next, select Console Application, Empty Project, and click Finish.

    Once your new project is loaded, you can see a list of project files in the Solution Explorer. If this docking window is not visible, go to View ➪ Solution Explorer. You can add new files or existing files to a project by right-clicking the project name in the Solution Explorer and then clicking Add ➪ New Item or Add ➪ Existing Item.

    Use Build ➪ Build Solution to compile your code. When it compiles without errors, you can run it with Debug ➪ Start Debugging.

    If your program exits before you have a chance to view the output, use Debug ➪ Start without Debugging. This adds a pause to the end of the program so you can view the output.

    At the time of this writing, Visual C++ 2017 does not yet automatically enable C++17 features. To enable C++17 features, in the Solution Explorer window, right-click your project and click Properties. In the properties window, go to Configuration Properties ➪ C/C++ ➪ Language, and set the C++ Language Standard option to ISO C++17 Standard or ISO C++ Latest Draft Standard, whichever is available in your version of Visual C++. These options are only accessible if your project contains at least one .cpp file.

    Visual C++ supports so-called precompiled headers, a topic outside the scope of this book. In general, I recommend using precompiled headers if your compiler supports them. However, the source code files in the downloadable source code archive do not use precompiled headers, so you have to disable that feature for them to compile without errors. In the Solution Explorer window, right-click your project and click Properties. In the properties window, go to Configuration Properties ➪ C/C++ ➪ Precompiled Headers, and set the Precompiled Header option to Not Using Precompiled Headers.

    GCC

    Create your source code files with any text editor you prefer and save them to a directory. To compile your code, open a terminal and run the following command, specifying all your .cpp files that you want to compile:

    gcc -lstdc++ -std=c++17 -o [source2.cpp …]

    The -std=c++17 option is required to tell GCC to enable C++17 support.

    For example, you can compile the AirlineTicket example from Chapter 1 by changing to the directory containing the code and running the following command:

    gcc –lstdc++ -std=c++17 -o AirlineTicket AirlineTicket.cpp AirlineTicketTest.cpp

    When it compiles without errors, you can run it as follows:

    ./AirlineTicket

    CONVENTIONS

    To help you get the most from the text and keep track of what’s happening, a number of conventions are used throughout this book.

    WARNING

    Boxes like this one hold important, not-to-be-forgotten information that is directly relevant to the surrounding text.

    NOTE

    Tips, hints, tricks, and asides to the current discussion are placed in boxes like this one.

    As for styles in the text:

    Important words are highlighted when they are introduced.

    Keyboard strokes are shown like this: Ctrl+A.

    Filenames and code within the text are shown like so: monkey.cpp.

    URLs are shown like this: www.wrox.com.

    Code is presented in three different ways:

    // Comments in code are shown like this. In code examples, new and important code is highlighted like this. Code that's less important in the present context or that has been shown before is formatted like this.

    Paragraphs or sections that are specific to the C++17 standard have a little C++17 icon on the left, just as this paragraph does. C++11 and C++14 features are not marked with any icon.

    SOURCE CODE

    As you work through the examples in this book, you may choose either to type in all the code manually, or to use the source code files that accompany the book. However, I suggest you type in all the code manually because it greatly benefits the learning process and your memory. All of the source code used in this book is available for download at www.wiley.com/go/proc++4e.

    Alternatively, you can go to the main Wrox code download page at www.wrox.com/dynamic/books/download.aspx to see the code that is available for this book and all other Wrox books.

    NOTE

    Because many books have similar titles, you may find it easiest to search by ISBN; for this book, the ISBN is 978-1-119-42130-6.

    Once you’ve downloaded the code, just decompress it with your favorite decompression tool.

    ERRATA

    At Wrox, we make every effort to ensure that there are no errors in the text or in the code of our books. However, no one is perfect, and mistakes do occur. If you find an error in one of our books, such as a spelling mistake or faulty piece of code, we would be very grateful for your feedback. By sending in errata, you may save another reader hours of frustration, and at the same time you will be helping us provide even higher-quality information.

    To find the errata page for this book, go to www.wrox.com and locate the title by using the Search box or one of the title lists. Then, on the book details page, click the Book Errata link. On this page you can view all errata that has been submitted for this book and posted by Wrox editors. A complete book list, including links to each book’s errata, is also available at www.wrox.com/misc-pages/booklist.shtml.

    If you don’t spot your error on the Book Errata page, go to www.wrox.com/contact/techsupport.shtml and complete the form there to send us the error you have found. We’ll check the information and, if appropriate, post a message to the book’s errata page and fix the problem in subsequent editions of the book.

    NOTES

    1  Apress, 2016. ISBN: 978-1-4842-1875-4.

    2  Depending on your version of VC++ 2017, you might not see any wizard. Instead, a new project will be created automatically containing four files: stdafx.h, stdafx.cpp, targetver.h, and .cpp. If that is the case, and you want to compile source code files from the downloadable source archive for this book, then you have to select those files in the Solution Explorer (View ➪ Solution Explorer) and delete them.

    PART I

    Introduction to Professional C++

    CHAPTER 1: A Crash Course in C++ and the Standard Library

    CHAPTER 2: Working with Strings and String Views

    CHAPTER 3: Coding with Style

    1

    A Crash Course in C++ and the Standard Library

    WHAT’S IN THIS CHAPTER?

    A brief overview of the most important parts and syntax of the C++ language and the Standard Library

    The basics of smart pointers

    WROX.COM DOWNLOADS FOR THIS CHAPTER

    Please note that all the code examples for this chapter are available as a part of the chapter’s code download on this book’s website at www.wrox.com/go/proc++4e on the Download Code tab.

    The goal of this chapter is to cover briefly the most important parts of C++ so that you have a base of knowledge before embarking on the rest of this book. This chapter is not a comprehensive lesson in the C++ programming language or the Standard Library. Certain basic points, such as what a program is and what recursion is, are not covered. Esoteric points, such as the definition of a union, or the volatile keyword, are also omitted. Certain parts of the C language that are less relevant in C++ are also left out, as are parts of C++ that get in-depth coverage in later chapters.

    This chapter aims to cover the parts of C++ that programmers encounter every day. For example, if you’ve been away from C++ for a while and you’ve forgotten the syntax of a for loop, you’ll find that syntax in this chapter. Also, if you’re fairly new to C++ and don’t understand what a reference variable is, you’ll learn about that kind of variable here, as well. You’ll also learn the basics on how to use the functionality available in the Standard Library, such as vector containers, string objects, and smart pointers.

    If you already have significant experience with C++, skim this chapter to make sure that there aren’t any fundamental parts of the language on which you need to brush up. If you’re new to C++, read this chapter carefully and make sure you understand the examples. If you need additional introductory information, consult the titles listed in Appendix B.

    THE BASICS OF C++

    The C++ language is often viewed as a better C or a superset of C. It was mainly designed to be an object-oriented C, commonly called as C with classes. Later on, many of the annoyances and rough edges of the C language were addressed as well. Because C++ is based on C, much of the syntax you’ll see in this section will look familiar to you if you are an experienced C programmer. The two languages certainly have their differences, though. As evidence, The C++ Programming Language by C++ creator Bjarne Stroustrup (Fourth Edition; Addison-Wesley Professional, 2013) weighs in at 1,368 pages, while Kernighan and Ritchie’s The C Programming Language (Second Edition; Prentice Hall, 1988) is a scant 274 pages. So, if you’re a C programmer, be on the lookout for new or unfamiliar syntax!

    The Obligatory Hello, World

    In all its glory, the following code is the simplest C++ program you’re likely to encounter:

    // helloworld.cpp #include int main() {     std::cout << Hello, World! << std::endl;     return 0; }

    This code, as you might expect, prints the message, Hello, World! on the screen. It is a simple program and unlikely to win any awards, but it does exhibit the following important concepts about the format of a C++ program:

    Comments

    Preprocessor directives

    The main() function

    I/O streams

    These concepts are briefly explained in the following sections.

    Comments

    The first line of the program is a comment, a message that exists for the programmer only and is ignored by the compiler. In C++, there are two ways to delineate a comment. In the preceding and following examples, two slashes indicate that whatever follows on that line is a comment.

    // helloworld.cpp

    The same behavior (this is to say, none) would be achieved by using a multiline comment. Multiline comments start with /* and end with */. The following code shows a multiline comment in action (or, more appropriately, inaction).

    /* This is a multiline comment.   The compiler will ignore it. */

    Comments are covered in detail in Chapter 3.

    Preprocessor Directives

    Building a C++ program is a three-step process. First, the code is run through a preprocessor, which recognizes meta-information about the code. Next, the code is compiled, or translated into machine-readable object files. Finally, the individual object files are linked together into a single application.

    Directives aimed at the preprocessor start with the # character, as in the line #include in the previous example. In this case, an #include directive tells the preprocessor to take everything from the header file and make it available to the current file. The most common use of header files is to declare functions that will be defined elsewhere. A function declaration tells the compiler how a function is called, declaring the number and types of parameters, and the function return type. A definition contains the actual code for the function. In C++, declarations usually go into header files, typically with extension .h, while definitions usually go into source files, typically with extension .cpp. A lot of other programming languages, such as C# and Java, do not separate declarations and definitions into separate files.

    The header declares the input and output mechanisms provided by C++. If the program did not include that header, it would be unable to perform its only task of outputting text.

    NOTE

    In C, the names of the Standard Library header files usually end in .h, such as , and namespaces are not used.

    In C++, the .h suffix is omitted for Standard Library headers, such as , and everything is defined in the std namespace or a sub-namespace of std.

    The Standard Library headers from C still exist in C++ but in two versions:

    The new and recommended versions without a .h suffix but with a c prefix. These versions put everything in thestdnamespace (for example,).

    The old versions with the .h suffix. These versions do not use namespaces (for example,).

    The following table shows some of the most common preprocessor directives.

    One example of using preprocessor directives is to avoid multiple includes, as shown here:

    #ifndef MYHEADER_H #define MYHEADER_H // … the contents of this header file #endif

    If your compiler supports the #pragma once directive, and most modern compilers do, then this can be rewritten as follows:

    #pragma once // … the contents of this header file

    Chapter 11 discusses this in more details.

    The main() Function

    main() is, of course, where the program starts. The return type of main() is an int, indicating the result status of the program. You can omit any explicit return statements in main(), in which case zero is returned automatically. The main() function either takes no parameters, or takes two parameters as follows:

    int main(int argc, char* argv[])

    argc gives the number of arguments passed to the program, and argv contains those arguments. Note that argv[0] can be the program name, but it might as well be an empty string, so do not rely on it; instead, use platform-specific functionality to retrieve the program name. The important thing to remember is that the actual parameters start at index 1.

    I/O Streams

    I/O streams are covered in depth in Chapter 13, but the basics of output and input are very simple. Think of an output stream as a laundry chute for data. Anything you toss into it will be output appropriately. std::cout is the chute corresponding to the user console, or standard out. There are other chutes, including std::cerr, which outputs to the error console. The << operator tosses data down the chute. In the preceding example, a quoted string of text is sent to standard out. Output streams allow multiple types of data to be sent down the stream sequentially on a single line of code. The following code outputs text, followed by a number, followed by more text:

    std::cout << There are << 219 << ways I love you. << std::endl;

    std::endl represents an end-of-line sequence. When the output stream encounters std::endl, it will output everything that has been sent down the chute so far and move to the next line. An alternate way of representing the end of a line is by using the \n character. The \n character is an escape sequence, which refers to a new-line character. Escape sequences can be used within any quoted string of text. The following table shows the most common ones:

    Streams can also be used to accept input from the user. The simplest way to do this is to use the >> operator with an input stream. The std::cin input stream accepts keyboard input from the user. Here is an example:

    int value; std::cin >> value;

    User input can be tricky because you can never know what kind of data the user will enter. See Chapter 13 for a full explanation of how to use input streams.

    If you’re new to C++ and coming from a C background, you’re probably wondering what has been done with the trusty old printf() and scanf() functions. While these functions can still be used in C++, I recommend using the streams library instead, mainly because the printf() and scanf() family of functions do not provide any type safety.

    Namespaces

    Namespaces address the problem of naming conflicts between different pieces of code. For example, you might be writing some code that has a function called foo(). One day, you decide to start using a third-party library, which also has a foo() function. The compiler has no way of knowing which version of foo() you are referring to within your code. You can’t change the library’s function name, and it would be a big pain to change your own.

    Namespaces come to the rescue in such scenarios because you can define the context in which names are defined. To place code in a namespace, enclose it within a namespace block. For example, the following could be the contents of a file called namespaces.h:

    namespace mycode {     void foo(); }

    The implementation of a method or function can also be handled in a namespace. The foo() function, for instance, could be implemented in namespaces.cpp as follows:

    #include #include namespaces.h void mycode::foo() {     std::cout << foo() called in the mycode namespace << std::endl; }

    Or alternatively:

    #include #include namespaces.h namespace mycode {     void foo()     {         std::cout << foo() called in the mycode namespace << std::endl;     } }

    By placing your version of foo() in the namespace mycode, you are isolating it from the foo() function provided by the third-party library. To call the namespace-enabled version of foo(), prepend the namespace onto the function name by using ::, also called the scope resolution operator, as follows:

    mycode::foo();    // Calls the foo function in the mycode namespace

    Any code that falls within a mycode namespace block can call other code within the same namespace without explicitly prepending the namespace. This implicit namespace is useful in making the code more readable. You can also avoid prepending of namespaces with the using directive. This directive tells the compiler that the subsequent code is making use of names in the specified namespace. The namespace is thus implied for the code that follows:

    #include namespaces.h using namespace mycode; int main() {     foo();  // Implies mycode::foo();     return 0; }

    A single source file can contain multiple using directives, but beware of overusing this shortcut. In the extreme case, if you declare that you’re using every namespace known to humanity, you’re effectively eliminating namespaces entirely! Name conflicts will again result if you are using two namespaces that contain the same names. It is also important to know in which namespace your code is operating so that you don’t end up accidentally calling the wrong version of a function.

    You’ve seen the namespace syntax before—you used it in the Hello, World program, where cout and endl are actually names defined in the std namespace. You could have written Hello, World with the using directive as shown here:

    #include using namespace std; int main() {     cout << "Hello, World!" << endl;     return 0; }

    A using declaration can be used to refer to a particular item within a namespace. For example, if the only part of the std namespace that you intend to use is cout, you can refer to it as follows:

    using std::cout;

    Subsequent code can refer to cout without prepending the namespace, but other items in the std namespace will still need to be explicit:

    using std::cout; cout << Hello, World! << std::endl;

    WARNING

    Never put a using directive or using declaration in a header file; otherwise, you force it on everyone who is including your header file.

    C++17 makes it easier to work with nested namespaces. A nested namespace is a namespace inside another one. Before C++17, you had to use nested namespaces as follows:

    namespace MyLibraries {     namespace Networking {         namespace FTP {             /* … */         }     } }

    This can be simplified a lot with C++17:

    namespace MyLibraries::Networking::FTP {     /* … */ }

    A namespace alias can be used to give a new and possibly shorter name to another namespace. For example: namespace MyFTP = MyLibraries::Networking::FTP;

    Literals

    Literals are used to write numbers or strings in your code. C++ supports a number of standard literals. Numbers can be specified with the following literals (the examples in the list represent the same number, 123):

    Decimal literal, 123

    Octal literal, 0173

    Hexadecimal literal, 0x7B

    Binary literal, 0b1111011

    Other examples of literals in C++ include

    A floating-point value (such as 3.14f)

    A double floating-point value (such as 3.14)

    A single character (such as 'a')

    A zero-terminated array of characters (such as character array)

    It is also possible to define your own type of literals, which is an advanced feature explained in Chapter 11.

    Digits separators can be used in numeric literals. A digits separator is a single quote character. For example,

    23'456'789

    0.123'456f

    C++17 adds support for hexadecimal floating-point literals—for example, 0x3.ABCp-10, 0Xb.cp12l.

    Variables

    In C++, variables can be declared just about anywhere in your code and can be used anywhere in the current block below the line where they are declared. Variables can be declared without being given a value. These uninitialized variables generally end up with a semi-random value based on whatever is in memory at that time, and are therefore the source of countless bugs. Variables in C++ can alternatively be assigned an initial value when they are declared. The code that follows shows both flavors of variable declaration, both using ints, which represent integer values.

    int uninitializedInt; int initializedInt = 7; cout << uninitializedInt << is a random value << endl; cout << initializedInt << was assigned an initial value << endl;

    NOTE

    Most compilers will issue a warning or an error when code is using uninitialized variables. Some compilers will generate code that will report an error at run time.

    The following table shows the most common types used in C++.

    1Requires an include directive for the header file.

    2Initialization of an std::byte requires direct list initialization with a single-element list. See the Direct List Initialization versus Copy List Initialization section later in this chapter for the definition of direct list initialization.

    NOTE

    C++ does not provide a basic string type. However, a standard implementation of a string is provided as part of the Standard Library, as described later in this chapter and in more detail in Chapter 2.

    Variables can be converted to other types by casting them. For example, a float can be cast to an int. C++ provides three ways to explicitly change the type of a variable. The first method is a holdover from C; it is not recommended but unfortunately still commonly used. The second method is rarely used. The third method is the most verbose, but is also the cleanest one, and is therefore recommended.

    float myFloat = 3.14f; int i1 = (int)myFloat;                // method 1 int i2 = int(myFloat);                // method 2 int i3 = static_cast(myFloat);  // method 3

    The resulting integer will be the value of the floating-point number with the fractional part truncated. Chapter 11 describes the different casting methods in more detail. In some contexts, variables can be automatically cast, or coerced. For example, a short can be automatically converted into a long because a long represents the same type of data with at least the same precision.

    long someLong = someShort;          // no explicit cast needed

    When automatically casting variables, you need to be aware of the potential loss of data. For example, casting a float to an int throws away information (the fractional part of the number). Most compilers will issue a warning or even an error if you assign a float to an int without an explicit cast. If you are certain that the left-hand side type is fully compatible with the right-hand side type, it’s okay to cast implicitly.

    Operators

    What good is a variable if you don’t have a way to change it? The following table shows the most common operators used in C++ and sample code that makes use of them. Note that operators in C++ can be binary (operate on two expressions), unary (operate on a single expression), or even ternary (operate on three expressions). There is only one ternary operator in C++, and it is explained in the Conditional Statements section later in this chapter.

    The following program shows the most common variable types and operators in action. If you are unsure about how variables and operators work, try to figure out what the output of this program will be, and then run it to confirm your answer.

    int someInteger = 256; short someShort; long someLong; float someFloat; double someDouble; someInteger++; someInteger *= 2; someShort = static_cast(someInteger); someLong = someShort * 10000; someFloat = someLong + 0.785f; someDouble = static_cast(someFloat) / 100000; cout << someDouble << endl;

    The C++ compiler has a recipe for the order in which expressions are evaluated. If you have a complicated line of code with many operators, the order of execution may not be obvious. For that reason, it’s probably better to break up a complicated expression into several smaller expressions, or explicitly group sub-expressions by using parentheses. For example, the following line of code is confusing unless you happen to know the C++ operator precedence table by heart:

    int i = 34 + 8 * 2 + 21 / 7 % 2;

    Adding parentheses makes it clear which operations are happening first:

    int i = 34 + (8 * 2) + ( (21 / 7) % 2 );

    For those of you playing along at home, both approaches are equivalent and end up with i equal to 51. If you assumed that C++ evaluated expressions from left to right, your answer would have been 1. C++ evaluates /, *, and % first (in left-to-right order), followed by addition and subtraction, then bitwise operators. Parentheses let you explicitly tell the compiler that a certain operation should be evaluated separately.

    Types

    In C++, you can use the basic types (int, bool, and so on) to build more complex types of your own design. Once you are an experienced C++ programmer, you will rarely use the following techniques, which are features brought in from C, because classes are far more powerful. Still, it is important to know about the following ways of building types so that you will recognize the syntax.

    Enumerated Types

    An integer really represents a value within a sequence—the sequence of numbers. Enumerated types let you define your own sequences so that you can declare variables with values in that sequence. For example, in a chess program, you could represent each piece as an int, with constants for the piece types, as shown in the following code. The integers representing the types are marked const to indicate that they can never change.

    const int PieceTypeKing = 0; const int PieceTypeQueen = 1; const int PieceTypeRook = 2; const int PieceTypePawn = 3; //etc. int myPiece = PieceTypeKing;

    This representation is fine, but it can become dangerous. Since a piece is just an int, what would happen if another programmer added code to increment the value of a piece? By adding 1, a king becomes a queen, which really makes no sense. Worse still, someone could come in and give a piece a value of -1, which has no corresponding constant.

    Enumerated types solve these problems by tightly defining the range of values for a variable. The following code declares a new type, PieceType, which has four possible values, representing four of the chess pieces:

    enum PieceType { PieceTypeKing, PieceTypeQueen, PieceTypeRook, PieceTypePawn };

    Behind the scenes, an enumerated type is just an integer value. The real value of PieceTypeKing is 0. However, by defining the possible values for variables of type PieceType, your compiler can give you a warning or an error if you attempt to perform arithmetic on PieceType variables or treat them as integers. The following code, which declares a PieceType variable, and then attempts to use it as an integer, results in a warning or an error on most compilers:

    PieceType myPiece; myPiece = 0;

    It’s also possible to specify the integer values for members of an enumeration. The syntax is as follows:

    enum PieceType { PieceTypeKing = 1, PieceTypeQueen, PieceTypeRook = 10, PieceTypePawn };

    In this example, PieceTypeKing has the integer value 1, PieceTypeQueen has the value 2 assigned by the compiler, PieceTypeRook has the value 10, and PieceTypePawn has the value 11 assigned automatically by the compiler.

    If you do not assign a value to an enumeration member, the compiler automatically assigns it a value that is the previous enumeration member incremented by 1. If you do not assign a value to the first enumeration member yourself, the compiler assigns it the value 0.

    Strongly Typed Enumerations

    Enumerations as explained in the previous section are not strongly typed, meaning they are not type safe. They are always interpreted as integers, and thus you can compare enumeration values from completely different enumeration types.

    The strongly-typed enum class enumerations solve this problem. For example, the following defines a type-safe version of the earlier-defined PieceType enumeration:

    enum class PieceType {     King = 1,     Queen,     Rook = 10,     Pawn };

    For an enum class, the enumeration value names are not automatically exported to the enclosing scope, which means that you always have to use the scope resolution operator:

    PieceType piece = PieceType::King;

    This also means that you can give shorter names to the enumeration values, for example, King instead of PieceTypeKing.

    Additionally, the enumeration values are not automatically converted to integers, which means the following is illegal:

    if (PieceType::Queen == 2) {…}

    By default, the underlying type of an enumeration value is an integer, but this can be changed as follows:

    enum class PieceType : unsigned long {     King = 1,     Queen,     Rook = 10,     Pawn };

    NOTE

    It is recommended to use the strongly-typed enum class enumerations instead of the type-unsafe enum enumerations.

    Structs

    Structs let you encapsulate one or more existing types into a new type. The classic example of a struct is a database record. If you are building a personnel system to keep track of employee information, you might want to store the first initial, last initial, employee number, and salary for each employee. A struct that contains all of this information is shown in the employeestruct.h header file that follows:

    struct Employee {     char firstInitial;     char lastInitial;     int  employeeNumber;     int  salary; };

    A variable declared with type Employee will have all of these fields built in. The individual fields of a struct can be accessed by using the . operator. The example that follows creates and then outputs the record for an employee:

    #include #include employeestruct.h using namespace std; int main() {     // Create and populate an employee.     Employee anEmployee;     anEmployee.firstInitial = 'M';     anEmployee.lastInitial = 'G';     anEmployee.employeeNumber = 42;     anEmployee.salary = 80000;     // Output the values of an employee.     cout << Employee: << anEmployee.firstInitial <<                             anEmployee.lastInitial << endl;     cout << Number: << anEmployee.employeeNumber << endl;     cout << Salary: $ << anEmployee.salary << endl;     return 0; }

    Conditional Statements

    Conditional statements let you execute code based on whether or not something is true. As shown in the following sections, there are three main types of conditional statements in C++: if/else statements, switch statements, and conditional operators.

    if/else Statements

    The most common conditional statement is the if statement, which can be accompanied by an else. If the condition given inside the if statement is true, the line or block of code is executed. If not, execution continues with the else case if present, or with the code following the conditional statement. The following code shows a cascading if statement, a fancy way of saying that the if statement has an else statement that in turn has another if statement, and so on:

    if (i > 4) {     // Do something. } else if (i > 2) {     // Do something else. } else {     // Do something else. }

    The expression between the parentheses of an if statement must be a Boolean value or evaluate to a Boolean value. A value of 0 evaluates to false, while any non-zero value evaluates to true. For example: if(0) is equivalent to if(false). Logical evaluation operators, described later, provide ways of evaluating expressions to result in a true or false Boolean value.

    Initializers for if Statements

    C++17 allows you to include an initializer inside an if statement using the following syntax:

    if ( ; ) { }

    Any variable introduced in the is only available in the and in the . Such variables are not available outside the if statement.

    It is too early in this book to give a useful example of this feature, but here is what it looks like:

    if (Employee employee = GetEmployee() ; employee.salary > 1000) { … }

    In this example, the initializer gets an employee and the condition checks whether the salary of the retrieved employee exceeds 1000. Only in that case is the body of the if statement executed.

    More concrete examples will be given throughout this book.

    switch Statements

    The switch statement is an alternate syntax for performing actions based on the value of an expression. In C++, the expression of a switch statement must be of an integral type, a type convertible to an integral type, an enumerated type, or a strongly typed enumeration, and must be compared to constants. Each constant value represents a case. If the expression matches the case, the subsequent lines of code are executed until a break statement is reached. You can also provide a default case, which is matched if none of the other cases match. The following pseudocode shows a common use of the switch statement:

    switch (menuItem) {     case OpenMenuItem:         // Code to open a file         break;     case SaveMenuItem:         // Code to save a file         break;     default:         // Code to give an error message         break; }

    A switch statement can always be converted into if/else statements. The previous switch statement can be converted as follows:

    if (menuItem == OpenMenuItem) {     // Code to open a file } else if (menuItem == SaveMenuItem) {     // Code to save a file } else {     // Code to give an error message }

    switch statements are generally used when you want to do something based on more than 1 specific value of an expression, as opposed to some test on the expression. In such a case, the switch statement avoids cascading if-else statements. If you only need to inspect 1 value, an if or if-else statement is fine.

    Once a case expression matching the switch condition is found, all statements that follow it are executed until a break statement is reached. This execution continues even if another case expression is encountered, which is called fallthrough. The following example has a single set of statements that is executed for several different cases:

    switch (backgroundColor) {     case Color::DarkBlue:     case Color::Black:         // Code to execute for both a dark blue or black background color         break;     case Color::Red:         // Code to execute for a red background color         break; }

    Fallthrough can be a source of bugs, for example if you accidentally forget a break statement. Because of this, compilers might give a warning if a fallthrough is detected in a switch statement, unless the case is empty as in the above example. Starting with C++17, you can tell the compiler that a fallthrough is intentional using the [[fallthrough]] attribute as follows:

    switch (backgroundColor) {     case Color::DarkBlue:         doSomethingForDarkBlue();         [[fallthrough]];     case Color::Black:         // Code is executed for both a dark blue or black background color         doSomethingForBlackOrDarkBlue();         break;     case Color::Red:     case Color::Green:         // Code to execute for a red or green background color         break; }

    Initializers for switch Statements

    Just as for if statements, C++17 adds support for initializers to switch statements. The syntax is as follows:

    switch ( ; ) { }

    Any variables introduced in the are only available in the and in the . They are not available outside the switch statement.

    The Conditional Operator

    C++ has one operator that takes three arguments, known as a ternary operator. It is used as a shorthand conditional expression of the form "if [something] then [perform action], otherwise [perform some other action]. The conditional operator is represented by a ? and a :. The following code outputs yes if the variable i is greater than 2, and no" otherwise:

    std::cout << ((i > 2) ? yes : no);

    The parentheses around i > 2 are optional, so the following is equivalent:

    std::cout << (i > 2 ? yes : no);

    The advantage of the conditional operator is that it can occur within almost any context. In the preceding example, the conditional operator is used within code that performs output. A convenient way to remember how the syntax is used is to treat the question mark as though the statement that comes before it really is a question. For example, Is i greater than 2? If so, the result is ‘yes’; if not, the result is ‘no.’

    Unlike an if statement or a switch statement, the conditional operator doesn’t execute code blocks based on the result. Instead, it is used within code, as shown in the preceding example. In this way, it really is an operator (like + and -) as opposed to a true conditional statement, such as if and switch.

    Logical Evaluation Operators

    You have already seen a logical evaluation operator without a formal definition. The > operator compares two values. The result is true if the value on the left is greater than the value on the right. All logical evaluation operators follow this pattern—they all result in a true or false.

    The following table shows common logical evaluation operators:

    C++ uses short-circuit logic when evaluating logical expressions. That means that once the final result is certain, the rest of the expression won’t be evaluated. For example, if you are performing a logical OR operation of several Boolean expressions, as shown in the following code, the result is known to be true as soon as one of them is found to be true. The rest won’t even be checked.

    bool result = bool1 || bool2 || (i > 7) || (27 / 13 % i + 1) < 2;

    In this example, if bool1 is found to be true, the entire expression must be true, so the other parts aren’t evaluated. In this way, the language saves your code from doing unnecessary work. It can, however, be a source of hard-to-find bugs if the later expressions in some way influence the state of the program (for example, by calling a separate function). The following code shows a statement using && that short-circuits after the second term because 0 always evaluates to false:

    bool result = bool1 && 0 && (i > 7) && !done;

    Short-circuiting can be beneficial for performance. You can put cheaper tests first so that more expensive tests are not even executed when the logic short-circuits. It is also useful in the context of pointers to avoid parts of the expression to be executed when a pointer is not valid. Pointers and short-circuiting with pointers are discussed later in this chapter.

    Functions

    For programs of any significant size, placing all the code inside of main() is unmanageable. To make programs easy to understand, you need to break up, or decompose, code into concise functions.

    In C++, you first declare a function to make it available for other code to use. If the function is used inside only a particular file, you generally declare and define the function in the source file. If the function is for use by other modules or files, you generally put the declaration in a header file and the definition in a source file.

    NOTE

    Function declarations are often called function prototypes or function headers to emphasize that they represent how the function can be accessed, but not the code behind it. The term function signature is used to denote the combination of the function name and its parameter list, but without the return type.

    A function declaration is shown in the following code. This example has a return type of void, indicating that the function does not provide a result to the caller. The caller must provide two arguments for the function to work with—an integer and a character.

    void myFunction(int i, char c);

    Without an actual definition to match this function declaration, the link stage of the compilation process will fail because code that makes use of the function will be calling nonexistent code. The following definition prints the values of the two parameters:

    void myFunction(int i, char c) {     std::cout << the value of i is << i << std::endl;     std::cout << the value of c is << c << std::endl; }

    Elsewhere in the program, you can make calls to myFunction() and pass in arguments for the two parameters. Some sample function calls are shown here:

    myFunction(8, 'a'); myFunction(someInt, 'b'); myFunction(5, someChar);

    NOTE

    In C++, unlike C, a function that takes no parameters just has an empty parameter list. It is not necessary to use void to indicate that no parameters are taken. However, you must still use void to indicate when no value is returned.

    C++ functions can also return a value to the caller. The following function adds two numbers and returns the result:

    int addNumbers(int number1, int number2) {     return number1 + number2; }

    This function can be called as follows:

    int sum = addNumbers(5, 3);

    Function Return Type Deduction

    With C++14, you can ask the compiler to figure out the return type of a function automatically. To make use of this functionality, you need to specify auto as the return type:

    auto addNumbers(int number1, int number2) {     return number1 + number2; }

    The compiler deduces the return type based on the expressions used for the return statements. There can be multiple return statements in the function, but they should all resolve to the same type. Such a function can even include recursive calls (calls to itself), but the first return statement in the function must be a non-recursive call.

    Current Function’s Name

    Every function has a local predefined variable __func__ containing the name of the current function. One use of this variable would be for logging purposes:

    int addNumbers(int number1, int number2) {     std::cout << Entering function << __func__ << std::endl;     return number1 + number2; }

    C-Style Arrays

    Arrays hold a series of values, all of the same type, each of which can be accessed by its position in the array. In C++, you must provide the size of the array when the array is declared. You cannot give a variable as the size—it must be a constant, or a constant expression (constexpr). Constant expressions are discussed in Chapter 11. The code that follows shows the declaration of an array of three integers followed by three lines to initialize the elements to 0:

    int myArray[3]; myArray[0] = 0; myArray[1] = 0; myArray[2] = 0;

    WARNING

    In C++, the first element of an array is always at position 0, not position 1! The last position of the array is always the size of the array minus 1!

    The next section discusses loops that you can use to initialize each

    Enjoying the preview?
    Page 1 of 1