360 likes | 372 Views
Explore program structure in C++, including free functions, main function, namespaces, classes, and more. Learn how to design single-file and multiple-files programs for efficient compilation, organization, and code reuse.
E N D
Advanced Program Design with C++ Part 1: Program Structure Joey Paquet, 2007-2019
Program structure free functions and main function compilation unit header file, implementation file namespaces Joey Paquet, 2007-2019
Structure of C++ programs • C++ is a superset of C • main() function is the program driver • Free functions • Data structures • A C program is a valid C++ program • Classes encapsulate other functions: Object-oriented • The main()function is a free function, not a class member • A valid program contains one and only one main()function Joey Paquet, 2007-2019
Single-file C++ program Joey Paquet, 2007-2019
Program structure: multiple files programs • Only simplistic programs are self-contained into one file • C++ is a language that specifies programming structures and includes only basic data structures and operators • Most programs require the use of libraries defined in other files • Complier will compile the library’s code and the program’s code, then the linker will link them • Unlike Java, a single C++ file can contain many classes/functions/data structures. • This leads to what is called physical design, i.e. decisions as to what entities are grouped within/across files. Joey Paquet, 2007-2019
Program structure: multiple files programs • Aside from having to use libraries, large programs need to be split into different files for various reasons: • Speed up compilation: The compiler may recompile only the files that changed since the last compilation. • Increase organization, decrease browsing time: Physically splitting your code along logical lines will make it easier to browse through the code to locate classes, functions, etc. • Facilitate code reuse: Modular physical design allows for grouping related entities and separating them from less related ones. Each group can then be logically designed to be reusable across different projects. Modular/reused code can be fixed, fixing all projects that use it. Joey Paquet, 2007-2019
compilation unit / component Joey Paquet, 2007-2019
Program structure: compilation unit • In C++, a compilation unit is a .cpp file • A file may contain several functions, data structures, or classes (unlike Java) • Each compilation unit is compiled individually into an object file • The object files typically have cross-references to other object files • The linker then attempts to resolve cross-references between the object files to form the unique executable file Joey Paquet, 2007-2019
Compilation units compilation and linkage: example Joey Paquet, 2007-2019
Program structure: compilation units as program component • Compilation units are separate program parts, or components • Kept in separate files • Compiled separately and linked together before the program runs • Compared to Java, C++ provides much more freedom to define what is a “part”, as it can be composed of a group of functions, data structures and classes • There is no such thing as Java-like “packages” in C++. • In C++, there is a separation between the declaration and the implementation of a component • Each component is a group of highly cohesive and highly coupledelements Joey Paquet, 2007-2019
Program structure: cohesion • Cohesion: The degree to which the elements of a component belong together in the achievement of a common goal. • Cohesion is increased if: • The functionalities embedded in a component have much in common. • The composing elements carry out a small number of related activities, by avoiding coarsely grained and/or unrelated sets of data. • Advantages of high cohesion: • Increased understandability of components (simpler, fewer operations). • Increased maintainability, because changes in one component require fewer changes in other components. • Increased reusability, as all that is needed to achieve a certain purpose is concentrated in one component, which is easier to reuse in another project. Joey Paquet, 2007-2019
Program structure: coupling • Coupling: the degree to which an element relies on other elements for its operation • Coupling is not a desired quality, but is a side-effect of defining different elements to carry a common task • Much related to cohesion: good modules exhibit high cohesion and high internal coupling • Coupling between components is what really needs to be avoided • Disadvantages of high coupling: • A change in one component forces a ripple effect of changes in other components • Assembly of a component requires more effort due to the increased inter-component dependency • A component might be harder to reuse and/or test because dependent components must be included Joey Paquet, 2007-2019
header files, implementation files Joey Paquet, 2007-2019
Program structure: header files and implementation files • Interface/header File (.hfile) • Contains class declarations with free functions and operators declarations • Useful to programmers, as it is an overview of a component that omits implementation details • Implementation File (.cppfile) • Contains free/member function definitions, i.e. the implementation code • The .cpp file is a compilation unit • General rules: • There should be a one-to-one relationship between a given .ccp file and a corresponding .h file, and they should have the same name. • The .cpp file “#includes” its corresponding.h file. • A.cpp file should never be “#included”, as it effectively merges two compilation units together. Joey Paquet, 2007-2019
Header file: example //This is the header file dtime.h. This is the interface for the class DigitalTime. //Values of this type are times of day. The values are input and output in //24 hour notation as in 9:30 for 9:30 AM and 14:45 for 2:45 PM. #ifndef DTIME_H #define DTIME_H #include<iostream> usingnamespacestd; classDigitalTime// This is a class declaration. { // It contains variable/function member declarations. public: // The function definitions are found in the corresponding .h file. DigitalTime(inttheHour, inttheMinute); DigitalTime( ); getHour( ) const; getMinute( ) const; void advance(intminutesAdded); void advance(inthoursAdded, intminutesAdded); friendbooloperator ==(constDigitalTime& time1, constDigitalTime& time2); friendistream& operator >>(istream& ins, DigitalTime& theObject); friendostream& operator <<(ostream& outs, constDigitalTime& theObject); private: int hour; int minute; staticvoidreadHour(int& theHour); staticvoidreadMinute(int& theMinute); staticintdigitToInt(char c); }; #endif//DTIME_H Joey Paquet, 2007-2019
Implementation file: example //This is the implementation file: dtime.cpp of the class DigitalTime.//It contains all the function definitions for the function declarations in the dtime.h file. //The interface for the class DigitalTime is in the header file dtime.h. //Note that there is no class declaration in this file. #include<iostream> #include<cctype> #include<cstdlib> usingnamespacestd; #include"dtime.h“ // All .cpp files need to #include their corresponding .h file //Uses iostream and cstdlib: DigitalTime::DigitalTime(inttheHour, inttheMinute) { if (theHour < 0 || theHour > 24 || theMinute < 0 || theMinute > 59){ cout << "Illegal argument to DigitalTime constructor."; exit(1); } else{ hour = theHour; minute = theMinute; } if (hour == 24) hour = 0; } // Syntax: ClassName::FunctionName. DigitalTime::DigitalTime( ){ hour = 0; minute = 0; } intDigitalTime::getHour( ) const{ return hour; } // All other member functions and operators definitions declared in dtime.h should be defined here } Joey Paquet, 2007-2019
Program structure: header files • Typically, for each component there is a x.cpp and a corresponding x.h file • Any program file using entities defined in a component will #include “x.h” • This way, during the compilation of this program file, the compiler compiles the declarations of all the entities of this component. • However, from the perspective of this program files, these entities have not been defined and need to be resolved. • The linker will later make the proper connections between the object files • In Java, much of this is solved by having files named after the single class they contain • The Java model is much easier to use, though less flexible than the C++ model Joey Paquet, 2007-2019
Program structure: header files inclusion • A component’s entities’ declarations (classes and free functions declaration) should always be in its header file. • Other components that use this component will “#include" it. There is different syntax to use if you are including a user-defined module, or an existing library module: #include “mymodule.h” • Quotes indicate a user-defined module • The compiler will find it in your project directories #include <mymodule.h> • < > indicate predefined library header file • The compiler will find it in the library directories • Using different search paths Joey Paquet, 2007-2019
Program structure: implementation files • A component’s implementation code should be in a .cppfile • Give the header file and the implementation file the same name • mymodule.h and mymodule.cpp • Not enforced by the compiler, but failure to do so is confusing to other programmers • A component is composed of classes and free functions • Implementation file must #includethe module’s header file, as it contains the module’s classes and data structures declarations • If it does not, the compiler will complain that its on entities are undeclared • cppfiles contain the executable code • Function definitions, including main(), free functions, member functions. Joey Paquet, 2007-2019
dealing with redundant includes Joey Paquet, 2007-2019
Program structure: redundant includes • Header files are typically included multiple times in an application’s code by different components • e.g., class declaration included by class implementation and main program file • Must only be compiled only once • else, multiply defined names • No guarantee which "#include" in which file the compiler might see first or how many files will end up including a particular header file • Use preprocessor directives • Instructs the compiler to read a header file only once Joey Paquet, 2007-2019
Program structure: redundant includes • Header file structure for file fname.h: #ifndef FNAME_H#define FNAME_H… //Content of header file…#endif • FNAME typically name of file for consistency and readability • This avoids multiple definitions from compiling the same header file more than once • May also use: #pragma once… //Content of header file… Joey Paquet, 2007-2019
Program structure: redundant includes • In fact, this is a specialized use of the conditional compilation preprocessor directive. • Conditional compilation: #ifdef x //or ifndef ... #else ... #endif • Can be used to switch between portions of code by switching on/off x, e.g. machine-dependent code. • Variables (e.g. x) are macro variables used/maintained by the preprocessor. Joey Paquet, 2007-2019
namespaces Joey Paquet, 2007-2019
Program structure: namespaces • Namespace: Collection of name definitions inside a program, potentially across different files that share the same namespace • For example, namespace “std” is common in many libraries #include <iostream>using namespace std; • Includes entire standard library of name definitions #include <iostream>using std::cin; using std::cout; • Can specify just the objects we want • Can be more efficient, as it avoids including things we don’t use Joey Paquet, 2007-2019
Program structure: namespaces • Used as a solution to resolve potential name clashes • Large programs use many classes and functions • As a program re-uses many other files, it increases the possibility of encountering entities that have the same name • Namespaces are meant to deal with this • A namespace can be turned "on" with the using namespacedirective • But how to “switch it off” after it has been activated? • You cannot, but conveniently, the using directive is effective only in the scope in which it is used (see next slide). Use different namespaces in separated code blocks. Though this solution has its limitations. • This is one reason why the use of using NS::namedirective is advocated over using namespace NS Joey Paquet, 2007-2019
Program structure: namespaces • Given namespaces NS1andNS2 • Both have void function myFunction() defined differently • If we want to use either definitions at different places in our program, we may do the following: { using namespace NS1; myFunction();} { using namespace NS2; myFunction();} Joey Paquet, 2007-2019
Program structure: global namespace • All code goes in some namespace • Unless specified, code belongs to the global namespace • No need for using directive • Global namespace is always available • Implied “automatic” using directive • But there is no way to “turn it off” • Thus, global namespace is prone to name clashes Joey Paquet, 2007-2019
Program structure: creating a namespace • To create a namespace: namespace Space1{Some_Code} • Places entities defined and/or declared in Some_Codeinto namespace Space1 • Can then be made available by : using namespace Space1 • And any of the entities defined (e.g. NSentity) in it can be made available by: using Space1::NSentity Joey Paquet, 2007-2019
Program structure: creating a namespace across header and implementation files • As seen earlier, header files and implementation files hold different parts of the definition/declaration of the entities in a component. • When using namespaces, the same namespace needs to be declared and used consistently in both the header file and the implementation file. • In the header file (declarations): namespace Space1{ void greeting();} • In the implementation file (definitions): namespace Space1{ void greeting() { cout << "Hello from namespace Space1.\n"; }} Joey Paquet, 2007-2019
Program structure: inline namespace qualification • Can specify where name comes from • Use the scope-resolution operator (::) • Only intended for one use (or few) • If overused, leads to less readable code NS1::fun1(); • Specifies that fun1()comes from namespace NS1 • Especially useful for parameters: intgetInput(std::istreaminputStream); • Parameter type definition found in istream’sstd namespace • Eliminates need for using directive or declaration Joey Paquet, 2007-2019
Program structure: namespaces example Joey Paquet, 2007-2019
Program structure: namespaces example Joey Paquet, 2007-2019
Program structure: unnamed namespace • Compilation unit: • A file, along with all files #included in the file • Thus, in you #include a .cpp file, you merge them into a single compilation unit. Avoid! • Every compilation unit has its own local unnamed namespace • Declared in the same way as a named namespace, but with no name • All names declared in an unnamed namespace are then local to the compilation unit • Use unnamed namespace to keep things "local" • Scope of unnamed namespace is compilation unit • Not same as global namespace • Global namespace: • No namespace grouping at all; global scope • Unnamed namespace: • Has namespace grouping, just no name; local scope Joey Paquet, 2007-2019
Program structure: global vs. unnamed namespace example #include <iostream> using namespace std; namespace { constinti = 4; // this is local } inti = 2; // this is globalint main() { cout << i << endl; // ERROR, i is ambiguous return 0; } Joey Paquet, 2007-2019
References • Walter Savitch, Absolute C++ (Chapter 1, 11), Addison-Wesley, 2006. • Bjarne Stroustrup, The C++ Programming Language (Chapters 2,6,14,15), Addison-Wesley, 2013. • Y. Daniel Liang, Introduction to Programming with C++ (Chapter 2, 13). Joey Paquet, 2007-2019