+ 43
[BLOG] C++ Bread && Butter (Part 1)
Part 1: Variables 1-1: Naming conventions (Good old advices) 1-2: Variable initialization (Don't mix up!) 1-3: Scope of variables (I can't see upstairs!) 1-4: Global variable madness! 1-5: My variable has auto type! 1-6: "auto" makes your life easier Dear friends, Please send your comments, questions, suggestions, feedbacks, and criticisms to [https://www.sololearn.com/Discuss/683525/c-bread-butter-q-a] Many thanks for your understanding. ;)
12 Antworten
+ 25
1-2: Variable initialization (Don't mix up!)
In C++, There are 3 ways to initialize variables:
1.The first one, known as "C-like initialization". (inherited from good old days)
format: dataType idName = initialValue;
[e.g. int foo = 7;]
---------------------
2. The second one, known as "constructor initialization".
format: dataType idName (initialValue);
[e.g. int foo (7);]
---------------------
3. The third one, known as "uniform initialization".(introduced by C++11)
format: dataType idName {initialValue};
[e.g. int foo {7};]
https://code.sololearn.com/c8hRdDAmlZoe
+ 22
1-3: Scope of variables(I can't see upstairs!)
⢠Scope:
The portion of the program where a variable can be visible and used is known as its scope. For example,a variable can be visible to a block (a section of code enclosed in curly braces), a routine, a class, or the whole program.
⢠Local & Global scopes:
A variable defined inside a particular code unit is said to be a "local variable" relative to that code unit. Conversly, a variable defined outside a particular code unit is a "global variable" relative to that code unit.
https://code.sololearn.com/cf5af71HUwSh
⢠nested scope:
Scopes can contain other scopes. The contained (or nested) scope is referred to as an "inner scope", the containing scope is the "outer scope".
>> 70% Pitfall: Which cout prints which variable?
/////////////////////
#include <iostream>
int main() {
int n = 2;
std::cout << n << endl; // output: 2
if (n > 0) {
int n = 5;
std::cout << n << endl; // output: 5
if (n == 5)
std::cout << n << endl; // output: 5
}
std::cout << n; // output: 2
}
/////////////////////
https://code.sololearn.com/cW84G16f37vn
This is called a "name-hiding" issue. As a "rule", C++ always use the variable "closest"
to the working code unit. Therefore, avoid variable names that hide names in outer scope(s).
(same name in each scope)
⢠Scope resolution operator:
There are some uses for scope resolution operator "::". One of which related to access to a global variable when there is a local variable with the same name.
//////////////
#include <iostream>
int n = 1000;
int main() {
int n = 2;
std::cout << n << endl; // output: 2
if (n > 0) {
int n = 5;
std::cout << n << endl; // output: 5
if (n == 5)
std::cout << ::n << endl; // output: 1000
}
std::cout << n; // output: 2
}
////////////////
https://code.sololearn.com/c7vyMeL1egYL
+ 21
1-1: Naming conventions (Good old advices)
There are a number of "generally" accepted conventions for naming variables. Following these conventions can improve the readability of a program.
⢠An identifier [e.g. variable name, function name etc] should give some indication of its meaning.
⢠Variable names normally are lowercase. [e.g. "result", not Result or RESULT]
⢠classes and constants you define usually begin with an uppercase letter.
[e.g. "class Spaceship" or "const double Pi = 3.14159265"]
⢠Identifiers with multiple words should visually distinguish each word.
[e.g. "interest_rate(Pascal case)" or "interestRate(Camel case)", not interestrate]
>> 100% tip: Try to avoid using "single and double underscore at the begining" of
an identifier name. Why?
Because, when naming an identifier, you want to avoid collision with compiler reserved names, operating system routines, and standard library. Also, it is a good practice to have a portable code to run on different compilers with different versions. So, Don't bother compiler and people! and,
1. Never start your identifier name with an underscore.
[e.g. int _xy = 0; // Bad
int x_y = 0; // Good
int xy_ = 0; // Good
int x_y_ = 0; // Good
int _x_y = 0; // Bad
int _xy_ = 0; // Bad
int _x_y_ = 0; // Bad ]
2. Never use two consecutive underscore nowhere in your identifier name.
[e.g. int __xy = 0; // Bad
int x__y = 0; // Bad
int xy__ = 0; // Bad
int __xy__ = 0; // Bad ]
+ 21
1-4: Global variable madness!
Somebody asked the guru what's the best naming prefix for a global variable?
The guru said " // "
Anyway!
>> 90% pitfall: But why?
⢠Side effect problem:
You might change the value of a global variable in one place and mistakenly think that it has remained unchanged somewhere else. (e.g. Leads to a unexpected program flow)
https://code.sololearn.com/ct1HRJfaxpRP
⢠Aliasing problems:
Aliasing refers to calling the same variable by two or more different names. This happens when a global variable is passed to a routine and then used by the routine both as a global variable and as a parameter.
///////////////////////
#include <iostream>
int gVar; // Global
void funksion(int &input) {
input = 10;
gVar = input + 5;
std::cout << "input = " << input << endl; // input = 15
std::cout << "gVar = " << gVar << endl; // gVar = 15
}
int main() {
gVar = 30;
funksion(gVar);
}
////////////////////////
You see, input and gVar are the same variables. cout prints a variable twice, even though they refer to two different names.
https://code.sololearn.com/cmAONio164sk
⢠Damage to program modularity:
$ What do you mean by modularity?
The essence of creating programs that are larger than a few hundred lines of code is managing complexity. The only way you can intellectually manage a large program is to break it into pieces so that you only have to think about one part at a time. Modularization is the most powerful tool at your hand for breaking a program into pieces.
$ So what?!
Global data pokes holes in your ability to modularize. If you use global data, can you concentrate on one routine at a time? No. You have to concentrate on one routine and every other routine that uses the same global data. Although global data doesnât completely destroy a programâs modularity, it weakens it, and thatâs reason enough to try to find better solutions to your problems.
+ 20
1-6: "auto" makes your life easier
⢠Using auto in Range-Based for Statement:
Wait a sec! First, tell me about range-based for loop. I've never heard it before!
A common use of the for statement is to iterate over a container of elements (e.g. array, string, vector, map etc). Currently, the syntax for built-in arrays and library containers is different (built-in arrays use an index or raw pointers, and container classes use iterators (**Don't forget to tell me about iterators later!) returned by the "begin" and "end" member functions.) In addition to providing simpler syntax, the new range-based for statement allows you to use the same syntax for iterating over both arrays and containers.
/////////////////////
#include <iostream>
int main() {
int arr[] = {1,2,3,4,5};
for (auto x : arr)
std::cout << x;
}
/////////////////////
https://code.sololearn.com/cHrg1B8n78tj
So, How does it work?
In this case, you use auto to let the compiler determine the type of x, which in this case will be int. On each iteration, the next number in arr[] will be copied into x. Thus, you can read this loop as saying, âFor every number x in the int arr[],â do something. The âsomethingâ in this case is printing the numbers.
>> 100% tip: If you want to change the value of the elements in arr[], you must define the
loop variable as a "reference type". (Remember that a reference is just another name for a given object). When you use a reference as your control variable, that variable is "bound" to each element in the sequence in turn. Using the reference, you can change the number to which the reference is bound.
/////////////////////
#include <iostream>
int main() {
int arr[] = {1,2,3,4,5};
for (auto &x : arr)
std::cout << ++x;
}
+ 20
>> 100% tip: But if you just want to read the value of the elements in arr[], it is Preferred to define the loop variable as a "constant reference". (Secure coding)
////////
#include <iostream>
int main() {
int arr[] = {1,2,3,4,5};
for (const auto &x : arr) // read-only
std::cout << x;
}
/////////
https://code.sololearn.com/cJsUa4uFFSg1
**Fasten your seatbelts! We are passing through STL, now!
> NOTICE: This is just scrach the surface of STL. The guru promised me to tell me more about that in the near future!
⢠Take a very brief look at the STL vector:
A vector is a collection of "objects", all of which have the same type (same as arrays). Every object in the collection has an associated index, which gives access to that object (again, same as arrays). A vector is often referred to as a container because it âcontainsâ other objects. To use a vector, you must include the appropriate header file.
(Vectors have many advantages over arrays. You'll see!)
//////////
#include <vector>
int main() {
// An empty integer vector
std::vector<int> vec1;
// A vector with 10 integer elements
std::vector<int> vec2(10);
// A vector with 10 integer elements, each having the value 100
std::vector<int> vec3(10, 100);
// A list-Initialized vector with 5 integer (C++11)
std::vector<int> vec4 = {10,20,30,40,50};
}
//////////
$ Well, how can I read/write to each element of a vector?
There are two choices in front of you:
1. Using traditional index-based for loop (No problem at all) Or range-based for loop
https://code.sololearn.com/cPAtzjF7HJiJ
+ 19
The same person asked the guru if global variables are so EVIL Why do we care about them?
The guru said "Because we need them"
So convincing!
>> 10% benefit: That's really it!
⢠Performance:
Global variables increase performance because they can be accessed directly by any function so, the overhead of passing data to functions is eliminated.(But is it really worth it?)
1-5: My variable has auto type!
Wow! Another cool feature. Can't wait, tell me about it real quick!
It is not uncommon to want to store the value of an expression in a variable. To
declare the variable, you have to know the type of that expression. When you write a
program, it can be surprisingly difficult (and sometimes even impossible) to determine the type of an expression. Under the new standard (C++11), you can let the
compiler figure out the type for you by using the "auto" type specifier. Unlike type specifiers, such as double (that name a specific type), auto tells the compiler to "deduce" the type from the initializer.
https://code.sololearn.com/cZL2piV6OPHf
>> 25% tip: By implication, a variable that uses "auto" as its type specifier must
have an "initializer". auto supports the declaration of multiple variables in one statement
[e.g. auto a = 10, b = 20;]. The initializers for all the variables in the declaration must have types that are consistent with each other.
[e.g. auto i = 0, *p = &i; // ok: i is int and p is a pointer to int
auto s = 0, pi = 3.14; // error: inconsistent types for s and pi ]
>> 15% tip: The auto keyword is meant to save time and ease the learning process.
>> 10% tip: auto can also be used with const. [e.g. const auto E = 2.7182;]
+ 19
4. dereference the current element of vec4 in each cycle.
⢠Using auto in iterators (Oh boy, you found the missing peace!):
When writing above code, you should be getting slightly annoyed. The variable itr is of the exact same type as the expression vec4.begin() with which it is being initialized, and the compiler knows what that type is. Therefore, for us to have to write std::vector<int>::iterator is redundant.Let's cut off the iterator length!
/////////////////////
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec4 = {10,20,30,40,50};
for (auto itr = vec4.begin(); itr != vec4.end(); ++itr)
st::cout << *itr << " ";
}
/////////////////////
https://code.sololearn.com/cPoNoo7Wvph9
+-------------+
End of Part 1
+-------------+
+ 18
2. Using iterators
Although you can use subscripts to access the elements in a vector, there is a more general mechanismâknown as iteratorsâthat you can use for the same purpose. Like pointers, iterators give you indirect access to an object. In the case of an iterator, that object is an element in a container. We can use an iterator to fetch an element and iterators have operations to
move from one element to another. (Sounds fancy! Tell me how to use it)
Unlike pointers, you do not use the address-of operator "&" to obtain an iterator. Instead, types that have iterators (like vectors) have members that return iterators. In particular, these types
have members named "begin()" and "end()". The begin() member returns an iterator that
denotes the first element, if there is one.
(Will discuss in more depth in future parts. I trust the guru!)
////
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec4 = {10,20,30,40,50};
// b denotes the first element in vec4
auto b = vec4.begin();
// e denotes one past the last element in vec4
auto e = vec4.end();
std::cout << *b << " " << *(e-1); // Output: 10 50
}
////
https://code.sololearn.com/csh3800ukrwD
Finally, you are ready to see the mighty iterator!
In fact, its structure is similir to a for loop. So, here we go!
////
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec4 = {10,20,30,40,50};
for (std::vector<int>::iterator itr = vec4.begin(); itr != vec4.end(); ++itr)
cout << *itr << " ";
}
////
https://code.sololearn.com/cz7D5BObJOE5
Phew! Would you mind to explain the structure a bit more?!
Let's breaking it down.
for (
1. std::vector<int>::iterator itr = vec4.begin();
2. itr != vec4.end();
3. ++itr
)
4. cout << *itr << " ";
1. declare an iterator called itr of type integer vector and initialize it to the first element
of vec4.
2. As a regular for loop, here is a condition.
3. iterator steps
+ 10
Wow, brilliant!!! đđđđđđ
+ 7
@Babak
Thank you, for sharing!
+ 4
@babak sir thank you so much for this wonderful information