- Pass by reference, then the variable can be changed inside the function.
- Pass a pointer. Compared with reference, pointer can be nullptr and do some check.
- Return an array(created on stack).
- Return a vector(created on heap, technically slower than array)
Special methods:
- tuple: can contain any number of variables, regardless of type. Also have std::make_tuple.
const std::tuple<std::string, std::string, int> a = {"abc", "def", 3};
std::cout << std::get<1>(a) << std::endl; // get "def"
- pair: like tuple, but only has two elements
const std::pair<int, std::string> a = {3, "abc"};
const auto b = std::make_pair(2, "def");
std::cout << a.first << std::endl; // get 3
std::cout << b.second << std::endl; // get "abc"
To avoid confusion, better return a struct contain all the return values.
An example of using templates:
#include <iostream>
#include <string>
template<typename T> // can also be written as template<class T>
void print(T t)
{
std::cout << t << std::endl;
}
int main()
{
print(5);
print(5.5f);
print<std::string>("abc"); // the correct type can also be added for readability
}
Another example of a class:
- size of array must be known when compiling, and template is evaluate at compiling, so it works(for N in the example).
- template can be 2 or more(for T and N).
template<typename T, int N>
class Array
{
private:
T array[N];
public:
int getSize() const
{
return N;
}
};
int main()
{
Array<int, 5> arr;
std::cout << arr.getSize() << std::endl;
}
Do not overuse templates.
Stack and heap are two areas in the RAM
- Stack is typically a predefined memory with size of around 2MB.
- Heap can grow and change as programme goes on.
Allocation
- Stack allocation: Continuous memory address and fast, just move of pointer according to bytes, memmory released at the end of scope. It is like one CPU instruction.
- Heap allocation: Memory is not continuous, memory has to be released manually(new - delete). It is a series of things.
Working Principle of new keyword
- Call the function malloc(memory allocate) to call the underline operating system or platform specific function.
- When start the appication, then get a certain amount of RAM, programme will maintain free list, which keeps tracking which block of memory is free.
- When asked for dynamic memory, it will go through the free list and find a block of memory at least as large as asked. Then return a pointer to that memory and record size of allocation...
- If asking for memory exceed free list of the initial allocation. The potential cost is huge.
Cache misses problem
preprocessor will go through all the code start with # (preproccesor directive), then pass to the compiler. macros: basically replace some texts in code(search and replace).
An example(but not recommend to overuse macros)]
#include <iostream>
#include <string>
#define WAIT std::cin.get()
#define LOG(x) std::cout << x << std::endl //function-like
int main()
{
LOG("Hello");
WAIT;
}
Preprocessor definitions:
#ifdef PROJ_DEBUG
#define LOG(x) std::cout << x << std::endl
#else
#define LOG(x)
#endif
Another manner: value of PROJ_DEBUG can be changed to enable or disable macros inside PROJ_DEBUG
#define PROJ_DEBUG 1
#if PROJ_DEBUG == 1
#define LOG(x) std::cout << x << std::endl
#else
#define LOG(x)
#endif
Even can define a function with several lines. (back slash) indicates enter.
#define WAIT int main()\
{\
std::cin.get();\
}
WAIT
auto can be used when declaring or copying variables. example(actually auto should not be used to replace type of short name.
int main()
{
auto a = 5;
auto b = 5L;
auto c = 5.5f;
auto d = "Hello";
auto e = a;
}
use auto to replace very long type name to add readability:
``` cpp
#include <iostream>
#include <string>
#include <vector>
int main()
{
std::vector<std::string> fruits;
fruits.push_back("apple");
fruits.push_back("orange");
// for (std::vector<std::string>::iterator it = fruits.begin(); it != fruits.end(); ++it)
// {
// std::cout << *it << std::endl;
// }
for (auto it = fruits.begin(); it != fruits.end(); ++it)
{
std::cout << *it << std::endl;
}
}