C++重新学习心得(1)(《C++Primer第四版》1-8章)

本文分享了重新学习C++的心得体会,重点介绍了C++语言的四大组成部分:C语言基础、面向对象特性、模板编程和STL标准库。此外还详细讨论了C++中的常见概念,如const对象、迭代器、指针与引用的区别等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++重新学习心得(1)

  这几天重新复习了一遍C++,现在想关于基础部分(《C++ Primer 第四版》中的1-8章内容),归纳一下自己的心得。
  在归纳之前先说一个比较好关于c++语言的总体思想(在《Effective C》中的第一章有介绍),将C++视为语言联合体。C++可以有四种子语言组成。
  第一种:C语言。归根结底C++依然是基于C的。
  第二种:C++的面向对象部分。也就是关于类和对象的部分,包括构造函数,析构函数,封装,继承,多态等。
  第三种:Template C++。C++的模板,泛型编程部分。
  第四种:STL标准库。容器,迭代器,和算法。
  在头脑中保持这四个子语言。将会发现C++理解容易的多。下面以列表的形式列出关于《C++ Primer 第四版》中1-8章的学习心得(因为之前系统的学习过C++,不会写一些语法知识,只写一些自己以前没有注意到的内容):
   1.以反斜杠”\”,将一行变成两行。
   2.012表示八进制。
   3.C++中尽量用#include<cname>而不是#include<name.h>
   4.c++中初始化不等于赋值。内置类型变量是否自动初始化取决于变量的定义的位置。在函数体外定义的变量都初始化成0,在函数体内定义的内置类型变量不进行自动初始化(P44)。
   5.extern关键字用于声明。程序中可以声明多次,但只能定义一次。任何在多个文件中使用的变量都需要有与定义分离的声明。在这种情况下,一个文件含有变量的定义,使用该变量的其他文件则包含该变量的声明(P50)。(声明:extern int i; 定义:extern int i=1;
   6.for(int i;i<10;i++){}这种常见的写法i的生存周期只在循环体中。
   7.const对象。任何时候如果能用const应该尽量用const。(1)在全局作用域声明的const变量是定义该对象的文件的局部变量,若其他文件要访问,则应该加extern关键字。如extern int i。(2)定义const对象包括基本数据或者引用或者指针时一定要初始化。当然有可能是声明如extern const int i,不要认为这是写错的(3)非const 引用不能 绑定const对象(4)非const引用只能绑定到与该引用同类型的对象,const引用则可以绑定到不同但相关的类型的对象或绑定右值(P52)(5)const指针。const在前(const int A)的为指向对象不能修改,const在后(int const A)的为指针的指向不能修改(6)把指向const的指针理解为“自以为指向const的指针”会更好,也就是说不能保证指向const的指针所指的对象不能修改,如其他非const指针也指向这个非const值,那么它的值可已通过这个非const指针修改。(7)总结:const的对象不管用何种方式都不能修改
   示例如下所示:

#include <iostream>
using namespace std;

int main()
{
    int B = 1;
    int C = 2;

    const int *D = &B;
    D = &C;  //可以改变指针的指向
    //*D = 3;  错误!表达式必须是可修改的左值,const放在前面指向的对象无法改变
    cout << *D << endl;

    int * const A = &B;
    *A = 3;   //可以改变指向的对象
    //A = &C;  错误!表达式必须是可修改的左值,const放在后面指向的对象无法改变
    cout << *A << endl;
    return 0;
}

   8.void *表明指针与一地址值先关,但不清楚存储在此地址上的对象。
   9.引用和指针的区别。引用可以看作别名。引用总是指向某个对象;定义引用时没有初始化是错误的。赋值行为的差异:给引用赋值修改的是该引用所关联对象的值,而并不是使引用与另一个对象关联。引用一定要初始化
   示例如下:

#include <iostream>
using namespace std;

int main()
{
    int B = 2;
    int &A=B;
    A = 1;
    cout << B<<endl;  // 1
}

   10.enum。其实每个enum都可以看作一个类。和其他类型一样,可以定义和初始化类型的对象,也可以用不同的方式来使用这些对象(P54)。
   11.类或结构体后面不要忘记加分号。
   12.用class和struct关键字定义类的唯一差别在于默认访问级别:默认情况下,struct的成员为public,而class的成员为private。其他则没有区别。struct结构体中也可以定义方法。
   13.typedef “原来的” “新的”。
   14.头文件用于声明而不是用于定义。因为头文件包含在多个源文件中,所以不应该含有变量或函数的定义。但头文件可以定义类,值在编译时就已知的const对象和inline函数。
   15.在头文件中不要忘记使用预处理器
   #ifndef XXX_H
   #define XXX_H
   ...
   #endif

   16.迭代器定义方法 vector<int>:: iterator iter。迭代器可以类比于指针。const_iterator可以保证所指向的值不改变。而 const vector<int>:: iterator iter则是不能改变迭代器指向,即iter++是不允许的。
   示例如下:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
    int a[] = { 1, 2, 3, 4, 5, 6 };
    vector<int> ve(a,a+6);
    ve.push_back(9);
    vector<int>::iterator iter=ve.begin();
    while (iter!=ve.end())
    {
        cout << *iter << endl;
        iter++;
    }
}

   17.数组的维数必须是常量表达式,且c++中一定要指定大小。int a[],或者 int s=10(没有const,不是常量表达式);int a[s]都是不行的。
   18. char a[3] ="asd"是错误的。因为字符串字面值包含一个额外的空字符(null)用于结束字符串。
   19.避免使用未初始化的指针。不管怎么样,int *p=NULL;(NULL就是0)。
   20.指针的大小固定(一般4个字节,是机器而定),类型声明是指向对象的类型而与指针的size无关。
   21.C++允许计算数组或对象超出末端的地址,但不允许对此地址进行解引用操作。也就可以用做与end()迭代器相似的功能。
   22.用const去代替宏#define。用内联函数的模板来代替宏函数(《Effective C》中Item2)。
   23.typedef string *pstring; const pstring cstr;pstring到底是什么指针?答案是const修饰的是指针而不是指向的对象,千万不要吧typedef当做文本扩展。
   24.允许new长度为0的数组。返回的指针也能做比较运算。
   25.delete [] a;中[]千万不能忘,回收数组时缺少空方括号,至少会导致运行时少释放内存空间,从而产生内存泄漏。
   26.别忘记标准库函数strlen返回的是字符串长度,并不包括字符串结束符,在获得的字符串长度上必须加1以便在动态分配时预留结束符的存储空间。
   27.数组可以初始化vector对象,但必须指出用于初始化式的第一个元素以及数组是最后一个元素的下一位置的地址。
   28.严格的说,c++没有多维数组,通常所指的多维数组即使数组的数组,而二维指针就是指针的指针。可以仿造char ** a={{..},{..},{..},};来理解。
   29.使用数组名,实际上将其自动转换为指向该数组第一个元素的指针。不管是几维数组。
   30.int *ip[4]; //指针数组 int (*ip)[4];//指向4个int型的数组的指针,也就是指针指向 int a[4]
   31.只有在必要时才使用后置操作符,前置操作需要做的工作更少。也就是++i比i++更加具有效率。但对于int型对象和指针,编译器可优化掉这项额外的工作。因此养成使用前置操作这个好习惯。
   32.int *pi =new int (); int *pi =new int前者会进行初始化而后者不会。因此尽量加括号。
   33.delete只能删除new分配的内存地址,一旦删除了指针所指向的对象,立即2将指针置为0(NULL)。
   34.强制转换用static_cast<类型>来代替(类型),用const_cast来转换掉表达式中的const性质。
   35.非引用的参数传递以及返回值都会调用拷贝构造函数,也就是说利用引用可以避免复制
   36.以指针的方式传递参数要注意,若非“引用指针”(int *&p),则实参值(调用函数原指针的指向)不会改变,但若函数中改变了指针指向的值,则调用函数原指针的指向值也会改变。
   示例:

#include <iostream>
#include <vector>
using namespace std;

void swap1(int *a, int *b)
{
    int *temp=a;
    a = b;
    b = temp;

}

void swap2(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main()
{
    int x = 1, y = 2;
    int *a = &x, *b = &y;
    swap1(a, b);
    cout << "*a=" << *a << ", *b=" << *b << endl;//*a=1 , *b=2
    swap2(a, b);
    cout << "*a=" << *a << ", *b=" << *b << endl;//*a = 2, *b = 1
    return 0;
}

   由上面的程序可以看出,swap1()函数并未起到调换的作用,这是因为函数调用过程中,形参对实参的复制,导致无论怎么修改都无法改变实参的值。而 swap2()修改的是指向的对象的值,因此可以改变。如下图所示。若需要swap1()改变,需要将形参改为(int &a,int &b),这样相当于传地址,因此修改形参等于修改实参。
   这里写图片描述
   37.若形参为非const引用,则实参不能为右值,比如void func(string &s),不能这么调用func (“aaaa”);所以更推荐用const引用作为参数。
   38.函数中不应该有vector或其他标准容器类型的形参(因为拷贝构造),通常倾向于传递相应的迭代器来传递容器,如begin和end。
   39.参数传数组传的其实就是指针,数组的长度编译器会自动忽略。有两种方法可以传递数组:(1)通过引用传递数组 void f(int (&arr)[10]),这时候数组大小会成为形参和实参的一部分(2)使用标准库规范,也就是像传递容器一样传递数组,给出begin和end。(3)最简单的方法,两个参数,数组名和数组大小(另:sizeof(数组名)是数组的大小)。
   40.千万不要返回局部对象的引用或指针,因为局部变量一旦离开函数,将被释放(后来发现并不一定具体请看http://blog.chinaunix.net/uid-27411029-id-3497902.html)。
   41.返回引用的作用常常是要当左值。如cout的重载。
   42.内联函数inline应该在头文件中定义。
   43.const成员函数,在成员函数后面加const。const改变了隐含的this形参的类型。另外const成员函数也只能调用其他const成员函数,如果用它们来调用非const成员函数,则是错误的。
   44.const可以用来重载,但不能基于指针本身是否为const来实现函数的重载。
   45.指向函数的指针,void(*p)(u1 opcode_len)也可以用typedef简化 typedef void(ByteCode::*jvm_func)(u1 opcode_len);(此为作者写的java虚拟机实现中的一个指针函数)。
   46.IO对象不可以复制或赋值。因此形参或返回类型也不能为流类型。如果需要传递或返回IO对象,则必须传递或返回指向该对象的指针或引用。如ofstream &print(&ofstream)
   47.在文件操作中不要忽略clear()函数的调用(P253)。特别是连续用同一个流对象打开两个以上文件。在关闭一个文件后,记得clear()重置一下state,否则对第二个文件进行读写时会出现问题。
   暂时就这些。
  
  
  
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值