C++复习笔记二

学习要求


1:C++基础

要求:

1.进一步了解和熟悉VC++6.0开发环境,学会在VC++6.0环境下调试程序;
2.熟悉C++中简单的标准输入输出函数的实用;
3.理解const修饰符的作用,并学会应用const修饰符;
4.理解内置(内联)函数的优缺点并学会使用内置函数;
5.理解和使用函数重载以及带默认参数的函数;
6.使用new和delete进行动态内存管理;
7.理解和使用引用。

解答:

1,2略

3.const限定符

(1)const的初始化

1.定义的时候直接初始化
2.类中使用初始化列表进行初始化

(2)const的位置

1.数据类型前

#include 
using namespace std;
int main()
{
    int a = 1;
    const int b = a;
    
    //b = 2;                      //错误:b不能修改
    return 0;
}

const限定后对指针的要求:

#include 
using namespace std;
int main()
{
    int a = 1;
    const int b = a;
    const int c = 2;
    int *p1 = &a;
    const int *p2 = &a;
    
    
    p1 = &a;
    //p1  = &c;                    //错误: 不能将const int *赋值给int * 
    p2 = &c;                        
    cout<<*p1<

2.指针const

#include 
#include 
using namespace std;
int main()
{
    int a = 1;
    const int b = a;
    const int c = 2;
    
    int *const p1 = &a;
    const int *p2 = &a;
    const int *const p3 = &a;
    
    //p1 = &c;    //指向不能改变 
    *p1 = c;
    
    p2 = &c;
    //*p2 = c;    //值不能改变 
    
    //p3 = &c;    //指向不能改变 
    //*p3 = c;    //值不能改变 
    
    cout<<*p1<<*p2<<*p3<

const <数据类型> * p:限定值不能修改,表示指针指向常量
<数据类型> *const p:限定指向不能修改
3.引用const

#include 
using namespace std;
int main()
{
    int a = 1;
    
    const int &d = a;
    //d = 3;         //错误:const限定引用不可修改 
    cout<

引用const经常用在函数参数里,节省空间时间
const限定的引用还有其他用途

#include 
using namespace std;
int main()
{
    int a = 1;
    double b = 2.333; 
    //int &c = b;    //错 
    const int &d = b;
    cout<

4.内联函数

详情:https://blog.csdn.net/u011327981/article/details/50601800

解决调用函数执行效率低的问题
定义内联函数:
inline写在定义函数体前面,不是声明前面
效果就是编译时直接替代函数调用
内联对于小函数体(10行以内)较好大函数体可能会使效率降低

#include 
using namespace std;

inline int Plus(int a,int b)
{
    return a+b;
} 

int main()
{
    int a=1;
    int b=2;
    cout<

编译等价于

#include 
using namespace std;

int main()
{
    int a=1;
    int b=2;
    cout<<(a+b);
    return 0;
}

5.函数重载&默认参数函数

(1)函数重载

方法:同名函数不同参

#include 
using namespace std;
int Plus(int a,int b)
{
    return a+b;
}
double Plus(double a,double b)
{
    return a+b;
}
int main()
{
    double a=1.111,b=2.222;
    cout<

(2)默认参数函数
1.默认参数右边都要是默认参数

#include 
using namespace std;
double Plus(double a=1.11,double b=2.22)
{
    return a+b;
}
int main()
{
    double a=1.111,b=2.222;
    cout<

2.定义时默认参数必须在非默认参数右边

#include 
using namespace std;
/* 
double Plus(double a=1.11,double b)    // 错误 
{
    return a+b;
}
*/
double Plus(double a,double b=2.22)   //正确
{
    return a+b;
}
int main()
{
    return 0;
} 

3.默认参数可以在声明或定义中定义,且只在其中一个定义

6.使用new和delete进行动态内存管理

用new的好处:对象可以调用构造函数
delete结束先调用析构函数再释放内存
new运算符返回该类型指针

#include 
using namespace std;
class Demo
{
    int a;
    public:
        Demo(int x):a(x){}
        ~Demo()
        {
            puts("析构"); 
        }
        int get()
        {
            return a;
        }
};
int main()
{
    int *p1 = new int;
    delete p1;
    
    int *p2 = new int[10];
    delete []p2;
    
    Demo *p3 = new Demo(2);
    cout<get()<

(7)引用

1.引用类似指针只不过引用不能更改引用对象,指针能更改指向
2.引用必须在定义时初始化


2.类的构建

要求:

1.类的定义;
2.类对象的使用;
3.类成员变量的定义和使用;
4.类成员函数的定义和使用;
5.理解类的作用域;
6.理解类的声明;
7.理解类中private和public权限的声明依据。

解答:

1,2,3,4略

(5)作用域

#include 
using namespace std;
class Demo
{
    #define bign 99
    public:
        const int maxn;
        Demo(); 
        ~Demo()
        {
        }
};
//在类体外用::作用域运算符定义函数 
Demo::Demo():maxn(999){}

int main()
{
    Demo d,*p=&d;
    cout<maxn<
(6)类的声明

声明以后只能用于定义类指针

#include 
using namespace std;
class Demo; 
class Point
{
    public:
        Demo *p;
};
class Demo
{
    public:
        int a;
        Demo(int x):a(x)
        {
        }
        int get()
        {
            return a;
        }
};

int main()
{
    Demo d(1);
    Point point;
    point.p=&d;
    cout<get()<
(7)类的成员属性(访问权限)
属性 访问范围 该种继承后的属性
public 该类中的函数,该类的对象,友元函数,子类函数 属性不变
protected 该类的函数,子类函数,友元函数 父类protected与public变为protected.private仍为private
private 该类函数,友元函数 都变为private
#include 
using namespace std;
class Demo
{
    private:
        int a;
        void out()
        {
            cout<

启示编写类是针对子类可能用到的private成员,留好接口(API)


3.构造函数与析构函数

要求

1.理解掌握this指针的作用和用法;
2.理解掌握构造函数的定义和作用;
3.掌握构造函数的使用;
4.理解掌握拷贝构造函数的定义和使用;
5.理解掌握构造函数的重载;
6.理解掌握析构函数的定义和使用。

解答

(1)this指针

指向该类自己的指针
可通过this指针访问该类成员

#include 
using namespace std;
class Demo
{
    public:
        int row;
        int col;
        /*                   //错误:函数参数自己给自己赋值 
        Demo(int row,int col)
        {
            row=row;
            col=col;
        }
        */ 
        Demo(int row,int col)
        {
            this->row=row;
            this->col=col;
        }
};
int main()
{
    Demo d(1,2);
    cout<

(2),(3)略

(4),(5)构造函数重载与拷贝构造函数

拷贝构造函数:拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:

  1. 通过使用另一个同类型的对象来初始化新创建的对象。
  2. 复制对象把它作为参数传递给函数。
  3. 复制对象,并从函数返回这个对象。
  • 如果在类中没有定义拷贝构造函数,编译器会自行定义一个。
  • 如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。
#include 
using namespace std;
class Demo
{
    public:
        int row;
        Demo(int row)
        {
            this->row=row;
        }
        Demo(const Demo &tem)//构造函数重载,拷贝构造函数 
        {
            this->row=tem.row;
        } 
};
int main()
{
    Demo d1(1),d2(d1); 
    cout<

(6)析构函数

类的生命周期结束后自动调用析构函数(delete也会)
可以在析构函数中释放申请的内存...

#include 
#include 
using namespace std;
class Demo
{
    public:
        int *p;
        Demo(int x)
        {
            p=(int *)malloc(sizeof(int)*x);
        }
        ~Demo()
        {
            free(p);
            cout<<"内存已释放"<

(4)对象传递和静态成员

要求

1.理解静态成员(静态数据成员、静态成员函数)的作用与使用;
2.理解友元(友元函数、友元类)的作用于使用;
3.理解常类型的使用。

解答

(1)静态成员

详情:http://c.biancheng.net/view/2228.html

  • 静态成员变量:该类的所有对象公用一个
  • 静态成员函数:只能访问静态成员,不需要通过对象调用,直接通过该类调用
#include 
#include 
using namespace std;
class Demo
{
    public:
        static int sum;
        int a;
        Demo(int x):a(x)
        {
            sum++;
        }
        static int get_sum()  //静态成员函数 
        {
            return sum;
        }
};
int Demo::sum=0;                  //静态数据成员初始化 
int main()
{
    Demo d1(1),d2(2);
    cout<
#include 
#include 
using namespace std;
class Demo
{
    public:
        static int sum;
        int a;
        Demo(int x):a(x)
        {
            sum++;
        }
        /*                 //错误 
        static int get_a()
        {
            return a;
        } */
        int get_sum()
        {
            //两种都可以 
            return this->sum;
            return sum;
        }
};
int Demo::sum=0;                  //静态数据成员初始化 
int main()
{
    Demo d1(1),d2(2);
    cout<
(2)友元函数,友元类

声明函数或类为该类友元后,该函数或类可以访问该类的私有成员

#include 
#include 
using namespace std;
class Demo
{
    public:
        int get()
        {
            return a;
        }
    private:
        int a;
        void set(int x)
        {
            a=x;
        }
    friend class demo;             //友元类 
    friend void set(int,Demo &);   //友元函数 
};
void set(int x,Demo &tem)
{
    tem.a=x;
}
class demo
{
    public:
        void set(int x,Demo &tem)
        {
            tem.a=x;
        }
        int get(Demo &tem)
        {
            return tem.a;
        }
};
int main()
{
    Demo d;
    set(1,d);
    cout<
(3)常类型const

1.函数体前加const(const成员函数)

  • 该函数不能修改类的数据成员
  • 该函数只能调用const成员函数
    2.函数前加const(返回值const函数),对于指针比较有用
    3.参数const,间前面const用法
#include 
using namespace std;
class Demo
{
    public:
        int a;
        Demo(int x):a(x){}
        void fix(int)const;   //注意声明方式 
        const int* get1();
        const int get2();
};
void Demo::fix(int x)const
{
    //this->a=x;        //不能修改类数据成员 
    int tem = x;
    tem = 66; 
}
const int* Demo::get1()
{
    int ans = this->a;
    return &ans;
}
const int Demo::get2()
{
    int ans = this->a;
    return ans; 
} 
int main()
{
    Demo d(1);
    d.fix(2);
    
    //int *p=d.get();  //错误,返回的类型的指针是const
    const int *p=d.get1(); 
    cout<<*p<

5.派生与继承---单重派生

要求:

1.理解继承的含义以及声明;
2.理解共有派生、私有派生和保护派生的含义以及使用;
3.理解单派生类中构造函数和析构函数的调用顺序。

解答:

(1)(2)略
(3)派生类中构造函数析构函数调用顺序
  1. 类自己的数据成员构造函数顺序:
    按定义顺序,与初始化列表顺序无关,再执行函数体(从上到下)
  2. 派生类构造函数
  • 由父到子
#include 
using namespace std;
class grandfather
{
    public:
        grandfather()
        {
            cout<<"grandfather构造"<

运行结果:

grandfather构造
father构造
son构造
son析构
father析构
grandfather析构

  • 多重继承只与继承时的顺序相关(从左到右),与初始化列表顺序无关
#include 
using namespace std;
class father1
{
    public:
        father1()
        {
            cout<<"father1构造"<

运行结果

father1构造
father2构造
son构造
son析构
father2析构
father1析构

换成如下函数运行结果相同

class son:public father1,public father2 
{
    public:
        son():father2(),father1()
        {
            cout<<"son构造"<
  • 既有继承又有自己成员时:
    先初始化继承,再按1的顺序初始化自己的成员
#include 
using namespace std;
class father1
{
    public:
        father1()
        {
            cout<<"father1构造"<

运行结果

father1构造
father2构造
Demo1构造
Demo2构造
son构造
son析构
Demo2析构
Demo1析构
father2析构
father1析构

3.析构函数
顺序与构造函数正好相反


6.派生与继承---多重派生

要求:

1.理解多重派生的定义;
2.理解多重派生中构造函数与析构函数的调用顺序;
3.理解多重派生中虚拟基类的作用;

解答:

(1)多重派生

有多个父(基)类,派生类同时继承多个基类

(2)略,见上
(3)虚拟基类

虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类(Virtual Base Class),本例中的 A 就是一个虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。

  • 换个角度讲,虚派生只影响从指定了虚基类的派生类中进一步派生出来的类,它不会影响派生类本身。

以下为没有用虚基类的继承
继承以后C中有2个a分属于B1,B2

#include 
using namespace std;
class A
{
    public:
        int a;
};
class B1:public A
{
    public:
        int b1;
};
class B2:public A
{
    public:
        int b2;
};
class C:public B1,public B2
{
    public:
        C(int aa,int bb1,int bb2,int cc)
        {
            //a = aa;             //有2个a,歧义 
            B1::a = aa;           //通过::访问2个不同的a 
            B2::a = aa+10;
            b1= bb1;
            b2= bb2;
            c = cc;
        }
        int c;
};
C demo(1,2,3,4);
int main()
{
    cout<

使用虚基类
C中只有1个a了,被B1,B2分享公用

#include 
using namespace std;
class A
{
    public:
        int a;
};
class B1:virtual public A
{
    public:
        int b1;
};
class B2:virtual public A
{
    public:
        int b2;
};
class C:public B1,public B2
{
    public:
        C(int aa,int bb1,int bb2,int cc)
        {
            a = aa;
            b1= bb1;
            b2= bb2;
            c = cc;
        }
        int c;
};
C demo(1,2,3,4);
int main()
{
    cout<

7.多态性---函数与运算符重载

要求:

1.理解动态联编和动态联编的概念;
2.理解掌握成员函数方式运算符重载;
3.理解掌握友元函数方式运算符重载;
4.理解掌握++、--、=运算符的重载。

解答:

(1)动态连编

编译程序在编译阶段并不能确切知道将要调用的函数,只有在程序运行时才能确定将要调用的函数,为此要确切知道该调用的函数,要求联编工作要在程序运行时进行,这种在程序运行时进行联编工作被称为动态联编。

虚函数

  • 通过基类对象指针是无法访问派生类的同名函数的
  • 虚函数用途:通过基类访问派生类定义的函数,指向基类的指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。
  • 基类声明的虚函数,在派生类中及时不用virtual,仍是虚函数
  • 虚函数只能通过指针/引用实现多态

1.定义一个函数为虚函数,不代表函数为不被实现的函数(可以有自己的实现)

2.定义它为虚函数是为了允许用基类的指针来调用子类的这个函数(提供了基类调用子类函数的方式)

3.定义一个函数为纯虚函数,代表函数没有被实现(声明后面接=0,例如:virtual func() = 0 此时派生类必须要实现此虚函数)
相当于规范要求派生类必须实现这个函数

4.具有纯虚函数的类是抽象类,不能用于生成对象(即不能实例化),只能派生,它派生的类如果没有实现纯虚函数,那么他的派生类还是抽象类。

以上内容来自:https://www.cnblogs.com/GyForever1004/p/8443241.html

只有虚析构函数,没有虚构造函数
用new创建派生类对象时,基类指针指向该对象,delete时通过静态联编,调用基类析构函数而不调用派生类的,产生内存泄漏.
设置成虚析构函数时,会通过动态联编,调用派生类析构函数

使用虚函数的情况

在你设计一个基类的时候,如果发现一个函数需要在派生类里有不同的表现,那么它就应该是虚的。从设计的角度讲,出现在基类中的虚函数是接口,出现在派生类中的虚函数是接口的具体实现。通过这样的方法,就可以将对象的行为抽象化。

参考:https://www.cnblogs.com/eilearn/p/9415786.html

#include 
using namespace std;
class A                                    //抽象类 
{
    public:
        A()
        {
            cout<<"A constructed"<fun();
    
    cout<sum(1,2)<
(2)(3)(4)两种方式进行运算符重载

成员函数||友元函数:
成员函数重载比友元函数重载少一个参数(左侧的参数是该对象本身)

流运算符必须通过友元函数重载
因为<<或>>左侧是cin,cout不是对象本身,除非要写成a<

双目运算符重载

#include 
using namespace std;
class Demo
{
    public:
        int row;
        int col;
    
    friend ostream& operator <<(ostream &,Demo &);
    friend istream& operator >>(istream &,Demo &);
    
    friend Demo operator +(Demo &,Demo &);             //友元方式重载 
    Demo operator -(Demo &);                           //成员函数方式 
};
ostream & operator <<(ostream &is,Demo &tem)
{
    cout<>(istream &is,Demo &tem)
{
    cin>>tem.row>>tem.col;
}
Demo operator +(Demo &a1,Demo &a2)
{
    Demo tem;
    tem.row=a1.row+a2.row;
    tem.col=a1.col+a2.col;
    return tem;
}
Demo Demo::operator -(Demo &a)
{
    Demo tem;
    tem.row=this->row-a.row;
    tem.col=this->col-a.col;
    return tem;
}
int main()
{
    Demo d1,d2;
    cin>>d1>>d2;
    cout<

其余重载详见:https://blog.csdn.net/liuyuchen282828/article/details/96474317


8.多态性--类型转换和虚函数

要求:

1.理解掌握运算符[]、()的重载;
2.理解类型转换,掌握类型转换函数的设计和使用;
3.理解和掌握虚函数的作用;
4.掌握利用虚函数实现C++的运行时多态性;
5.理解纯虚类和抽象类。

解答:

1.[]和()重载
(1)[]重载:

下标运算符[ ]必须以成员函数的形式进行重载

格式:
返回值类型 & operator[ ] (参数);
或:
const 返回值类型 & operator[ ] (参数) const;

以上第一种不仅可以访问元素,还可以修改元素.
第二种只能访问元素,适用于const对象

#include 
using namespace std;
class Demo
{
    public:
        Demo():bign(3){}
        const int bign;
        int Array[3];

        int & operator [](int i)//左值返回引用类型
        {
            if(i<0||i>=bign)
            {
                return Array[0];
            }
            else
            {
                return Array[i];
            }
        }
};

int main()
{
    Demo d;
    d[0]=0;
    d[1]=1;
    d[2]=2;

    cout<
(2)重载()
  • 由于()既可以作为左值又可以作为右值,所以重载括号运算符函数通常返回引用
  • 括号运算符只能被重载为类的非静态成员函数
  • 重载括号运算符函数的参数个数没有限制,甚至没有参数都可以。
    <1>一般带参()运算符
#include 
#include 
using namespace std;
class Demo
{
    public:
        double matrix[3][3];

        double & operator()(int row,int col)     //左值返回引用类型
        {
            if(row<3&&row>=0&&col<3&&col>=0)
                return matrix[row][col];
            else
                exit(0);
        }
};
int main()
{
    Demo d;
    d(1,1)=4;
    cout<
  • 注:左值既可以放=左边也可以放右边,右值只能放=右边

<2>类型转换:

  • 把一个类类型转换成基本数据类型或另一个类类型
  • 不能有返回值,不能有参数,只返回要转换的数据类型
  • 只能重载为非静态成员函数

格式:
operator type(){return type对象;}

#include 
#include 
using namespace std;
class Demo
{
    public:
        Demo(double money):money(money){}
        double money;
        operator int ()            //右值
        {
            return (int)money;
        }
};

int main()
{
    Demo d(1.111);
    //两种用法
    cout<<(int)d<

(2),(3),(4),(5) 前面均有,略


8.模板类

要求:

1.能够使用C++模板机制定义重载函数。
2.能够实例化及使用模板函数。
3.能够实例化和使用模板类。
4.应用标准C++模板库(STL)通用算法和函数对象实现查找和排序。

解答:

你可能感兴趣的:(C++复习笔记二)