[C++] Chap 14 - Separate Compilation and Namespaces
[C++] Chap 14 - Separate Compilation and Namespaces
Seperate Compilation
- Program parts
- kept in separate files
- Compiled separatedly
- Linked together before program runs
- Class Definitions
- Separate from “using” programs
- Build Library of classes → Re-used by many diffrerent programs, just like predefine libraries
- Encapsulation principle
- Separate how class is used by programmer from details of class’s implementation
- “Complete” separation : change to implementation does not impact on any other programs
- Basic OOP principle
Class separation
- Interface file
- Contains class definition with function and operator declarations/prototypes
- Users “see” this
- Implementation File
- Contains member function definitions
Class header files
- Class interface always in header file : use
.hnaming convention - Programs that use the class will “include” it
#include "myclass.h"- Quote(””) indicate you wrote the header
- Use <> with predefined library header file
Class Implementation files
- Class implementation in
.cppfile - Typically give interface and implementation file same name.
- All class’s member function are defined here
- Implementation file must
#includethe class’s header file - Class header file #included by:
- Implementation file
- Program file (Often called “application file” or “driver file”
- The one that has the main function
Multiple compiles of Header files
- Header files typically included multiple times. (e.g. class interface included by class implementation and program file)
- But it must be compiled once.
- There’s no guarantee which #include in which file the compiler might see first.
- Use preprocessor to tell compiler to include header only once
Using #ifndef
1
2
3
4
#ifndef FNAME_H
#define FNAME_H
...
#endif
- FNAME is typically the name of the file
- This syntax avoids multiple definitions of header file
Namespaces
- A collection of name definitions
- Class definitions, variable declarations
- Programs use many classes and functions, Commonly they may have same names
- Namespaces deal with this. Namespaces can be “on” or “off”
using namespace std;: Makes all definitions in std namespcae available
Namespace std
- Contains all names defined in many standard library files
#include <iostream>- Places all name definitions(
cin,cout, etc) into std namespace - Must specify the namespace for program to access names
- Places all name definitions(
Global Namespace
- All code goes into some namespace
- Unless specified : global namespace
- No need for using directive
- Global namespace always available
Specifying Namespaces
1
2
3
4
5
6
7
8
{
using namespace NS1;
myFunction();
}
{
using namespace NS2;
myFunction();
}
usingdirective has block-scope
Creating a Namespace
1
2
3
4
5
6
namespace Namespacename
{
Some Code
}
using namespace Namespacename;
Multiple names
- If name is defined in both namespaces
- Error if used in a same scope
- But still can use both namespaces
- Must specify which namespace is used at what time
Using Declarations
using NAME_SPACE::ONE_NAME;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
namespace NS1 {
void myFunction() {
cout<<"foo_ns1"<<endl;
}
void fun1() {
cout<<"foo1"<<endl;
}
}
namespace NS2 {
void myFunction() {
cout<<"foo_ns2"<<endl;
}
void fun2() {
cout<<"foo2"<<endl;
}
}
int main() {
{
using NS1::fun1;
using NS2::fun2;
fun1();
fun2();
}
{
using NS1::myFunction;
using NS2::myFunction;
myFunction();//ILLEGAL
}
{
using namespace NS1;
using namespace NS2;
//Legal but..
myFunction(); //ILLEGAL
NS1::myFunction(); //Legal
}
}
Qualifying names
- Specify where name comes from with qualifier and scope-resolution operator
- Used if you only intend to use one time
Unnamed Namespaces
- Compilation unit : A file along with all files that are #includeded in the file, such as interface header file
- Every compilation unit has an unnamed namespace
- Unnamed namespace enables using functions without qualifications within the compilation unit
Nested Namespaces
- Legal to nest namespaces
1
2
3
4
5
6
7
8
9
10
namespace S1
{
namespace S2
{
void sample() {}
}
}
S1::S2::sample();
//Qualify names twice
- Namespace defininitions are placed inside namespace groupings
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
namespace NS{
void myFunction();
}
namespace NS{
void myFunction() {
some defs..
}
}
int main() {
NS::myFunction();
using namespace NS;
myFunction();
using NS::myFunction;
myFunction();
}
RAII
- Resource Acquisition is Initialization
- Principle : a resource’s lifetime = an object’s lifetime
- Acquisiation (Initialization) : Acquire resource (allocate memory, open a file) inside the constructor of an obj
- Release(Destruction) : You automatically release that resource inside the destructor of the same object
Without RAII
- Manuel management
1
2
3
4
5
6
void process(bool fail) {
File* f = newFile("data.txt");
if (fail) return;
delete f;
}
// if Fails, never deletes f
- Every path must rememeber to delete
- Early retrun / exception makes silent leak
- unique_ptr
1
2
3
4
5
void process(bool fail) {
auto f = make_unique<File>("data.txt");
if (fail) return;
}
// No explicit delete but destructor frees it
- No delete anywhere
- Same behavior, zero chance of a leak
Smart Pointer
- unique_ptr : one exclusive owner
- shared_ptr : shared, reference-counted
- weak_ptr: non-owning observer
unique_ptr
- Exactly one owner at a time
- Move-only : copying is disabled
- Transfer with std::move
- Freed automatically when it dies
- Zero overhead versus a raw pointer
1
2
3
4
std::unique_ptr<T> uptr = std::make_unique<T>()
//Make unique_ptr of type T
auto anotherPtr = std::move(uptr);
if (uptr == nullptr) //True
Rule of Zero
- build from self-managing memebers
- The compiler generates the rest correctly
This post is licensed under CC BY 4.0 by the author.