C++基础学习(一)

1. 数据类型

1.1 基本内置类型

  我们通过下列代码可知电脑上的基本数据类型的大小:

 1 #include<iostream>  
 2 #include<string>  
 3 #include <limits>  
 4 using namespace std;
 5 
 6 int main()
 7 {
 8 
 9     cout << "char: 		" << "所占字节数:" << sizeof(char);
10     cout << "	最大值:" << (numeric_limits<char>::max)();
11     cout << "		最小值:" << (numeric_limits<char>::min)() << endl;
12     
13     cout << "float: 		" << "所占字节数:" << sizeof(float);
14     cout << "	最大值:" << (numeric_limits<float>::max)();
15     cout << "		最小值:" << (numeric_limits<float>::min)() << endl;
16     
17     cout << "int: 		" << "所占字节数:" << sizeof(int);
18     cout << "	最大值:" << (numeric_limits<int>::max)();
19     cout << "	最小值:" << (numeric_limits<int>::min)() << endl;
20     
21     cout << "unsigned: 	" << "所占字节数:" << sizeof(unsigned);
22     cout << "	最大值:" << (numeric_limits<unsigned>::max)();
23     cout << "	最小值:" << (numeric_limits<unsigned>::min)() << endl;
24     
25     cout << "long: 		" << "所占字节数:" << sizeof(long);
26     cout << "	最大值:" << (numeric_limits<long>::max)();
27     cout << "	最小值:" << (numeric_limits<long>::min)() << endl;
28     
29     
30     cout << "double: 	" << "所占字节数:" << sizeof(double);
31     cout << "	最大值:" << (numeric_limits<double>::max)();
32     cout << "	最小值:" << (numeric_limits<double>::min)() << endl;
33     return 0;
34 }

1.2 const 和 auto 

1. const 基本介绍

  const对象一旦创建后其值就不能再改变,所以const对象必须初始化。编译器在编译过程会把用到该变量的地方替换成相应的值,为了执行上述的替换,编译器必须知道变量的初始值。

1 const int i = get_size();  // 运行时初始化
2 const int j = 3; // 编译时初始化
3 const int k;// 错误

  默认情况下,const对象仅在文件中有效。当多个文件中出现了同名的const变量时,其实等同于在不同文件中分别定义了独立的变量。如果想在在文件中得到共享,即在一个文件中定义const,而在其他多个文件中声明并使用它。可以使用 extern 关键词,对于const变量不管是声明还是定义都添加 extern 关键词,这样只需定义一次就可以了。

  但是需要注意下面这个例子

 1 struct A {
 2     int* ptr;
 3 };
 4 
 5 int main()
 6 {
 7     int k = 5, r = 6;
 8     const A a = { &k };// const声明的变量必须初始化
 9     a.ptr = &r; // !error
10     *a.ptr = 7; // no error
11 }

  可以看到,const 修饰 a 之后其实要求的是 ptr (存储一个地址)不能变,但其实可以改变 *ptr—— c++ 保持物理常量行不变,逻辑常量性不保证。

2. 类中的const变量

 1 struct A {
 2     const int i; // 构造类是初始化。非静态
 3 };
 4 
 5 struct B {
 6     static const int i;// 静态
 7 };
 8 
 9 const int B::i = 3; // 常在类中中直接声明
10 
11 int main()
12 {
13     A bf = { 1 };
14  
15 }

3. 顶/底层 const  和 auto

  下面是顶层 const 和 底层 const 的实例:

 1 /*
 2  3     顶层const:指针本身是个常量
 4     底层const:指针所指的对象是一个常量
 5     
 6  7     auto一般会忽略顶层const,会保留底层const
 8 */
 9 
10 #include"iostream"
11 using namespace std;
12 int main()
13 {
14     const int i = 19; //i值不能改变,顶层const
15     const int *p = &i;//p值可以改变,底层const(*P值不能改变->指针所指的对象是个常量)
16     
17     int z = 18;
18     int *const o = &z;//o值不能改变,顶层const
19     int t = 13;
20     //o = &t; //错误,因为o是顶层,o值不能改变
21 
22     //打印类型
23     cout << typeid(i).name() << endl;//int
24     cout << typeid(p).name() << endl;//int const *
25     cout << typeid(*p).name() << endl;//int 
26     cout << typeid(o).name() << endl << endl;// int *
27 
28     auto a = o;
29     int y = 12;
30     a = &y; // a值可改变,说明忽略了o的顶层const。对比第20行的o
31     cout << "*a=" << *a << "	 a类型" << typeid(a).name()<< endl;
32 
33     auto b = p;
34     b = &y; //p是底层const,b也是底层const,b值可改变,但是*b不能改变
35     //*b = 9; //报错,b是底层const,b指针所指的对象不能改变。---》说明auto保留了底层const
36     cout << "*b=" << *b << "	 b类型" << typeid(b).name() << endl;
37 
38 
39     return 0;
40 }

 4. const 修饰函数

  const 可以修饰类返回值(一般用于类返回值是指针的形式。修饰返回值的函数没有意义)。也可以写在函数后(如第7行),标是允许修改类的成员变量(mutable关键字修饰的成员变量除外).

 1 struct A {
 2     int i;
 3     mutable int j;
 4 
 5     void f() const{
 6         //this->i++;// error, const 修饰的函数不能改变类成员变量。
 7         this->j++;// 除非是使用mutable关键字
 8     }
 9 
10     const int* g() {  
11         int k = 2;
12         return &k;
13     }
14 
15     int *const h() {  
16         int k = 2;
17         return &k;
18     }
19 
20 };
21 
22 int main()
23 {
24     A a = { 1 , 2};
25     a.f();
26 
27     /*
28         a.g() 返回的是 const int *k 类型。是底层const, *k 不能改变
29     */
30     const int* k = a.g();
31     //*k = 3; // 非法
32     k = new int();// 合法
33 
34 
35     /*
36         a.h() 返回的是  int *const b 类型。是顶层const, b 不能改变
37     */
38     int* const b = a.h();
39     *b = 3; // 合法
40     //b = new int();// 非法
41 }

  

2. vector 类型

2.1 vector 基本操作

  • v.empty() 
  • v.size()  返回 vector 对象中元素个数,返回值类型由 vector 定义的 size_type 决定 (vector<int>::size_type)
  • v[n] 返回 v 中第 n 个位置上元素的引用
  • v1 = v2 用 v2 中元素的拷贝替换 v1 中的元素
  • v1 == v2 当且仅当元素数量 and 元素值相同

2.2 访问 vector 

  • 可以通过下标形式访问 vector,但是不能通过下标形式添加元素而是,push_back()
  • 使用 range-base-for 或者 iterator 遍历
 1 #include<iostream>
 2 #include<vector>
 3 #include<typeinfo>
 4 
 5 int main() {
 6     std::vector<std::string> v1{ "Tom","Jack","Michael" };
 7     std::vector<int> v2(5, 7);
 8     
 9     for (auto& i : v1) {
10         std::cout << i << std::endl;
11     }
12     
13     std::cout << "====================" << std::endl;
14 
15     for (auto k = v2.begin(); k != v2.end(); ++k) {
16         std::cout << *k << std::endl;
17     }
18 
19     std::cout << "====================" << std::endl;
20     
21     v1.push_back("good dog");
22     std::vector<std::string>::iterator iter = v1.begin();
23     for (; iter != v1.end(); ++iter) {
24         std::cout << *iter << std::endl;
25     }
26     
27     std::cout << "====================" << std::endl;
28     
29     v2[0] = 9;
30     for (decltype(v2.size()) i = 0; i < v2.size(); ++i) {  // v2.size() 返回的是无符号数(>=0),可以用 int,但需要保证不能为负数,否则无符号数会自动合法话。可运行36~38行
31         std::cout << v2[i] << std::endl;
32     }
33 
34     std::cout << "====================" << std::endl;
35     // 会输出 “大于”
36     /*decltype(v2.size()) i = -1;
37     if( i > 10)
38         std::cout << "大于" << std::endl;*/ 
39 
40 
41     return 0;
42 }

3. 迭代器

  • 我们知道已经可以使用下标运算符来访问 string 对象或 vector 对象的元素(因为只有一些标准库类型有下标运算符,并非全都如此),但是还有一种更通用的机制可以实现——使用迭代器。
  • 如果容器为空,可通过 iter.end() != iter.begin() 来判断,例如:有 string str 对象,则 if (str.end() != str.begin()) {.....} 来保证 str 不为空
  • 凡使用了迭代器的循环体,都不要向迭代器所属容器中添加元素

3.1 迭代器和指针

  我们可能有疑问,如果是 vector<int> v1 = {1,2,3},那么通过迭代器迭代时,每次的迭代器是 1,2,3 三个数字的地址吗(因为我们可以通过 *iter 来取得数值 )?答案是不是的。

 1 #include<iostream>
 2 #include<vector>
 3 #include<typeinfo>
 4 
 5 int main() {
 6     std::vector<int> v = { 1 ,2,4 };
 7     for (auto& i : v)  
 8         std::cout << typeid(i).name() << &i <<std::endl;
 9     for (auto k = v.cbegin(); k != v.cend(); ++k)  // cbegin()得到const_iterator。begin()得到iterator。如果只进行读操作可用前者
10         std::cout << typeid(k).name() <<std::endl;
11     return 0;
12 }

  我们可以比较 range-based-for 和 iterator-for 的输出类型,其中第八行 &i 输出了 v 中的元素的地址。

  那迭代器到底是啥?此处有解释。

  range based for loop vs regular iterator for loop

  

  

原文地址:https://www.cnblogs.com/KongHuZi/p/11188991.html