恶补C++基础知识

一些基本的概念和术语:
1、源文件。程序源码存在一个或多个文件中,这些程序文件通常被称为源文件(source file)。不同编译器使用不同的后缀名,比如.cc、.cxx、.cpp、.cp及.c
2:通过命令行运行编译器。

输入和输出
C++中并没有定义任何输入输出语句,而是通过一个全面的标准库来提供IO机制。比如iostream库。一个流就是一个字符序列。流表示随着时间的推移,字符是顺序产生和消耗的。
输入流:istream
输出流:ostream

cin:标准输入
cout:标准输出
cerr:警告和错误信息
clog:程序运行时的一般性信息

输出运算符(<<),输入运算符(>>)。比如输出运算符,运算符接受两个运算对象,左侧必须是一个iostream对象,右侧的运算符是要打印的值。
std::<<endlendl是一个操纵符。写入endl的效果是结束当前行。并且将与设备关联的缓冲区(buffer)中的内容刷新到设备中。缓冲刷新操作可以保证到目前为止程序所产生的所有输出都真正的写入到输出流中,而不仅仅是停留在内存中等待写入流。也就是说执行输出操作的时候,数据仅仅是写入到了缓冲区,而并没有刷新到设备中,调用了endl,不仅实现了换行,同时实现了把数据刷新到缓冲区的操作。
前缀std:: 它表示名字endl是定义在std的命名空间(namespace)中的。命名空间可以帮助我们避免不经意的名字定义冲突,以及使用库中相同名字导致的冲突。标准库定义的名字一般都在std中。
:: 表示作用域运算符

在C++语言中,名字有作用域,对象有生命周期
名字的作用域是程序文本的一部分,名字在其中可见。
对象的生命周期是程序执行过程中对该对象存在的一段时间。


初始化和赋值


复合类型——–引用和指针

引用:
(1)引用即别名。引用不是对象
(2)引用只能绑定在对象上,不能绑定与字面值或某个表达式的计算结果绑定在一起。
(3)一般在初始化变量时,初始值会被拷贝到新建的对象中。
(4)在定义引用时,程序把引用和它的初始值绑定在一起,而不是将初始值拷贝给引用。一旦初始化完成,引用将和它的初始值对象一直绑定在一起。
(5)因为无法将引用重新绑定到另外一个对象,因此引用必须初始化

#include <iostream>
#include <cstdlib>
using namespace std;
int main(){
    int i = 12;
    int &refi = i;      //refi是i的引用

    refi = 34;          //改变refi的值,即使改变i的值
    cout << refi << " " << i << endl;       //34  34 

    return 0;
}



指针:
(1)指针本身就是个对象,允许对指针赋值和拷贝
(2)指针无需在定义时赋初值。和其他内置类型一样,在块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值。
(3)指针存放某个对象的地址。可以使用取地址符(&)来获取地址
(4)如果指针指向了一个对象,可以使用解引用符(*)来访问该对象
(5)void*可以存放任意对象的地址。但是我们对该地址中到底是个什么类型的对象并不了解。
(6)**表示指向指针的指针。***表示指向指针的指针的指针,以此类推
(7)指向指针的引用。引用本身不是一个对象,所以不存在指向引用的指针。但是指针是对象,因此存在对指针的引用。

#include <iostream>
#include <cstdlib>
using namespace std;
int main(){
    int i = 23;
    int *p1 = &i;   //p1被初始化,存放i的地址 
    int *p2;        //p2被初始化,没有指向任何对象 
    p2 = p1;        //p2和p1指向同一个对象i 

    *p2 = 12;       //p2所指向的对象被改变了(此处是指针指向的对象被改变了) 
    p2 = 0;         //p2不指向任何对象(此处是指针改变了) 

    return 0;
}
//指向指针的引用
#include <iostream>
#include <cstdlib>
using namespace std;
int main(){
    int i = 1024;
    int *p;
    int *&r = p;    //r是对指针p的引用。此处从右向左解读,&r可以知道r是一个引用,*&r可以知道 r引用的是一个指针 

    r = &i;         //r引用了一个指针,也就是p指向i
    *r = 0;         //解引用r得到i,也就是将i的值改为0

    return 0;
}


指向类的指针
一个指向 C++ 类的指针与指向结构的指针类似,访问指向类的指针的成员,需要使用成员访问运算符 ->

#include <iostream>

using namespace std;

class Box{
   public:
      // 构造函数定义
      Box(string s){
         cout <<"Constructor called." << endl;
      }

      string Volume(){
         return s + "test";
      }
    private:
        string s;
};

int main(void){
   Box Box1("box1");    
   Box Box2("box2");    
   Box *ptrBox;          

   // 保存第一个对象的地址
   ptrBox = &Box1;
   cout << "Volume of Box1: " << ptrBox->Volume() << endl;

   // 保存第二个对象的地址
   ptrBox = &Box2;
   cout << "Volume of Box2: " << ptrBox->Volume() << endl;

   return 0;
}

输出:

Constructor called.
Constructor called.
Volume of Box1: test
Volume of Box2: test



局部静态对象:
(1)把局部变量定义成static类型从而得到的对象。
(2)局部静态对象在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止时才被销毁,在此期间,即使的对象所在的函数结束执行也并不会对它有影响。
(3)如果静态局部变量没有显示的初始值,它将执行值初始化。内置类型的局部静态变量初始化为0

函数

函数返回类型:不能是数组或者函数,但可以是指向数组或函数的指针。

函数的声明:
(1)也称作函数原型。
(2)函数的声明没有函数体,用一个分号代替即可。
(3)一般在头文件中进行函数声明,在源文件中进行函数定义。
(4)含有函数声明的头文件应该被包含到定义函数的源文件中。

访问修饰符:一般在数据定义为私有的,相关的函数定义为公有的。

修饰符 本类 类的外部
public 可访问 可访问
private 可访问 不可访问(只有类和友元函数可以访问)
protected 可访问 不可访问(在派生类即子类中可以访问)

类的析构函数
类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。


友元函数
类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。
如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend。
友元函数不是类的成员,因此友元函数没有this指针。只有成员函数才有this指针。


内联函数
C++ 内联函数是通常与类一起使用。如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。
对内联函数进行任何修改,都需要重新编译函数的所有客户端,因为编译器需要重新更换一次所有的代码,否则将会继续使用旧的函数。
如果想把一个函数定义为内联函数,则需要在函数名前面放置关键字 inline,在调用函数之前需要对函数进行定义。如果已定义的函数多于一行,编译器会忽略 inline 限定符。
在类定义中的定义的函数都是内联函数,即使没有使用 inline 说明符
程序在编译器编译的时候,编译器将程序中出现的内联函数的调用表达式用内联函数的函数体进行替换,而对于其他的函数,都是在运行时候才被替代。这其实就是个空间代价换时间的i节省。所以内联函数一般都是1-5行的小函数。


继承中的特点:
(1)public 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:public, protected, private
(2)protected 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:protected, protected, private
(3)private 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:private, private, private

(1)private 成员只能被本类成员(类内)和友元访问,不能被派生类访问;
(2)protected 成员可以被派生类访问。

函数指针

#include <iostream>
using namespace std;

class Father
{
    public:
        void func()
        {
            cout << "Father func" << endl;
        }
        void  func2()
        {
            cout << "Father func2" << endl;
        }

};

class Son:public Father
{
    public:
        void func2()
        {
            cout << "Son func2" << endl;
        }

};


typedef void(Father::*PF1)();
typedef void(Son::*PF2)();

int main()
{
    Father  f;
    Son s;

    PF1 pf1 = &Father::func;
    (f.*pf1)();
    (s.*pf1)();

    pf1 = &Father::func2;
    (f.*pf1)();
    (s.*pf1)();

    PF2 pf2 = &Son::func2;
    (s.*pf2)();

    return 0;
}

控制台打印:

Father func
Father func
Father func2
Father func2
Son func2



函数指针,对象指针

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


//类指针的用法
class  CObjPoin
{
    public:
        typedef int (CObjPoin::*Proc)(int);             //*Proc:表示一个指向方法名的指针
    public:
        int add(int b)
        {
            return (a + b);
        }
        int sub(int b)
        {
            return (a - b);
        }
    public:
        int a;
};


//调用类指针
void  CalcClassPoint()
{
    CObjPoin cObj;
    cObj.a = 2;

    CObjPoin::Proc myproc[2] = {&CObjPoin::add, &CObjPoin::sub};      //Proc:表示指针,因此数组中的内容应该是函数名的引用

    for(int i = 0;i < 2;i++)
    {
        cout << (cObj.*myproc[i])(20) << endl;            //*myproc[i]表示解引用。得到函数名。
    }
}




//函数指针用法
int mul(int a,int b)
{
    return a * b;
}
int ormeth(int a,int b)
{
    return a ^ b;
}

typedef int (*MethodHandler) (int lhs,int rhs);


//调用函数指针
void TestMethod()
{
    MethodHandler myHandler[2] = {mul,ormeth};
    for(int i = 0;i < 2;i++)
    {
        cout << myHandler[i](2,4) << endl;
    }

}







//运算符重载
class CCompare
{
    public:
        bool operator () (int lhs,int rhs)
        {
            return lhs > rhs;
        }
};

//运算符重载的调用
void  CalcOperator()
{
    int arr[] = {10,3,5,7,23,12,43,2};
    int length = sizeof(arr) / sizeof(arr[0]);
    sort(arr,arr + length,CCompare());
    for(int i = 0;i < length;i++)
    {
        cout << arr[i] << " ";
    }

}




int main()
{
    CalcClassPoint();
    TestMethod();
    CalcOperator();
}



何时使用继承,何时使用模板?
当对象的类型不影响类中函数的行为时,就要使用模板来生成这样一组类。
当对象的类型影响类中函数的行为时,就要使用继承来得到这样一组类。

猜你喜欢

转载自blog.csdn.net/qq_36748278/article/details/79905269