C++复习

目录

一、struct 和 class 的区别

二、面向对象和面向过程的区别

三、描述面向对象的特征

四、内联函数的宏定义的区别

五、引用和指针的区别

六、函数重载

七、类和对象的区别

八、一个类会默认生成几个函数?

九、构造函数和析构函数

十、初始化列表(冒号语法)

十一、缺省函数:带默认值的函数

十二、拷贝构造函数

十三、new、delete和malloc、free的区别

十四、static静态数据成员和static成员函数

十五、友元

十六、运算符重载

十七、this指针

十八、const指针


一、struct 和 class 的区别

     首先,在C中和C++中,struct(结构体)的情况是有所不同的:

struct的用法:

1.在C语言中,结构体不能为空;

2.在C语言中,struct只是一个结构体名,在C++中是一个类型

3.在C++中,如果struct是空,则大小为1个字节

4.struct内存对齐

5.在C的结构体中,不能写函数,在C++中struct中可以写函数

struct和class的区别:

1.struct是一个值类型,class是一个引用类型

2.在C++中,struct默认是公有的(public),class默认是私有的(private)

3.struct不能被继承,class可以被继承

二、面向对象和面向过程的区别

面向对象就是对实物进行抽象化,而面向过程就是自底向上的编程过程

面向过程的性能比面向对象高,因为类在调用过程中需要实例化,消耗资源较大;但没有面向对象易维护、易复用、易扩展,可维护性差,不易修改;

面向对象易维护、易复用、易扩展,面向对象由封装、继承、多态的特性,可以设计出耦合度低的系统,使系统更加灵活,更易维护;缺点是性能比面向过程低。

三、描述面向对象的特征

封装、继承、多态

封装:把数据(属性)和函数(操作、方法)和成一个整体,使用类和对象实现

继承:继承和派生,两面一体,子类继承了基类;基类派生了子类

多态:由继承产生的相关而不同的类,其对象对相同的消息会产生不同的反应

四、内联函数的宏定义的区别

(1)内联函数在编译时展开,带参的宏定义在预编译时展开

(2)内联函数直接嵌入到目标代码中,带参的宏是简单的做文本替换

(3)内联函数有类型检测、语法判断等功能,宏只是替换

五、引用和指针的区别

指针:是一种数据类型,对于一个类型T,T*就是指向T类型的指针类型,T*这个变量保存了一个          T类型变量的地址

引用:引用是一个对象的别名,与原本的对象没有区别(只是给它起了一个另外的名字)

区别:1.指针可以为空,引用不可以为空,不存在空引用

                  引用是对象的别名,对象不存在,就不可能有别名

           2.引用不能改变指向,而指针可以改变指向,指向别的对象

           3.引用的大小是它所指向变量的大小,引用只是一个别名,与原本的对象没有区别;指针是指针本身的大小,(32位4字节,68位8字节)

           4.指针有多级指针,引用没有多级引用

六、函数重载

1.函数名相同

2.参数列表不同

3.不能仅通过函数返回值来区分

4.const函数可以构成重载

        成员函数后面可以加const——称作常成员函数

七、类和对象的区别

类是抽象的,不占内存,对象是具体的,占用内存空间

八、一个类会默认生成几个函数?

        8个,暂时只需要6个

1.构造函数;2.析构函数;3.拷贝构造函数;4.赋值运算符重载函数;5.取地址操作符的重载函数

6.const修饰的取地址操作符的重载函数

九、构造函数和析构函数

构造函数:

为什么需要构造函数?

需要一个函数来初始化数据成员,自己定义的成员函数需要调用才能使用,会滞后(已经有了人,才有年龄,不符合实际),而构造函数会在生成对象的同时初始化数据成员

当对象创建时,自动的调用本类的构造函数

1.构造函数是一个特殊的成员函数,函数名与类名相同,无返回类型,可以带参,可以重载

2.构造函数是一个特殊的公有函数,没有特殊情况,必须是公有的(public)

3.在对象出来前自动调用,而且在生存期中只调用一次

        如果程序员没有定义,则类会生成一个默认的构造函数(无参)

        如果需要给数据成员开辟空间,需要自己定义构造函数,此时不会生成默认构造函数

在main前可以运行一段代码吗?
可以
遇到对象要调构造函数,所以定义一个全局变量,可以说明在main()函数前
可以运行一段代码
全局变量在main()函数前就要分配空间,同时调用构造函数

调用构造函数3步骤
1.传参
2.根据数据成员在类里面的声明顺序(开辟空间)用冒号语法后面的值进行初始化
3.执行构造函数的函数体

析构函数:

析构函数:
1.析构函数名字与类名相同,在前面加字符“~”

2.析构函数无返回类型

3.析构函数不能带参,不能重载,一个类有且只有一个析构函数

4.对象注销时(生存期结束时),系统会自动调用析构函数

5.如果程序员没有提供,系统会提供一个默认的析构函数:

        如果在构造函数中有内存开辟,要手动释放,必须写析构函数

        如果没有内存的手动开辟,可以不写析构函数(但一般都要写)

6.执行顺序:析构函数的执行顺序与构造函数相反,先构造的后析构

十、初始化列表(冒号语法)

冒号语法:成员初始化列表
能等号赋值的,就可以用冒号语法
不是数组(拷贝)指针(动态的开辟空间)就可以使用冒号语法

十一、缺省函数:带默认值的函数

一般情况下,函数调用时实参个数与形参相同,为了方便使用函数,定义了具有缺省参数的函数,这种函数调用时,实参个数可以与形参不同

缺省参数指在定义函数时为形参指定缺省值(默认值)

void Fun (int a, int b = 23 ,int c = 80)

缺省函数的调用:

1.如果给出了实参值,将实参传给形参进行调用

2.如果不给出实参,则按缺省值进行调用

3.缺省实参可以是一个表达式,在函数被调用时改表达式被求值(表达式必须有意义

4.缺省实参可以通过函数调用给出

int my_rand()

{
        srand(time(NULL));

        int ra = rand() % 100;

        return ra;
}

void fun(int a, int b = my_rand())

{
        cout << " a = " << a << " b = " << b << endl;

}

void main()

{

        fun(12);
}

缺省参数可以有多个,但所有的缺省参数必须放在参数表的右侧(先定义所有的非缺省参数,再定义缺省参数,这是因为在函数调用时,参数自左向右组个匹配)

void fun(int a, int b= 23 ,int c = 8000)
{
    cout << "a = " << a << " b = " << b << " c = "<< c << endl;
}
void main()
{
    fun(12);
    fun(10,20);
    fun(10,20,30);
    fun(10, , 30);//error
}

十二、拷贝构造函数

拷贝构造函数——复制构造

作用:用已有对象的数据成员去依次初始化新对象的数据成员

如果不写拷贝构造函数,会生成默认拷贝构造函数,

默认拷贝构造函数:把我的拷贝一份去构造你,结果一样

分为深拷贝和浅拷贝

浅拷贝:用已有对象去构造(初始化)新对象

深拷贝:资源的拷贝,给新对象的指针开辟和原对象开辟的一样的空间(拷贝资源)

class Student
{
  public:
        Student(int num = 1001, const char* name = " "):m_num(num)
        {
             m_nanme = new char[strlen(name)+1];
            strcpy(m_name, name);
        }

        //浅拷贝
        Student(const Student& s) :m_num(s.m_num), m_name(s.m_name)
        {
            cout << "Student(s)" << endl;
        }

        //深拷贝
         Student(const Student& s) :m_num(s.m_num), m_name(s.m_name)
        {
            m_name = new char[strlen(s.m_name) + 1];
            strcpy(m_name, s.m_name);
        }
};

调用拷贝构造函数的三种情况:

1.用一个原有的对象去初始化一个新对象

2.函数传参是类类型的值传递(指针和引用不用调用拷贝构造函数),用一个类去拷贝另一个类
   实参->形参(函数的形式参数)
   类类型:class A
       void fn(A s)//类类型,s是形参

3.函数返回值是类类型的值类型

class A
{
public:
    A(int i = 0) :m_i(i) { cout << "A" << m_i << endl; }//构造函数
    A(const A& s) :m_i(s.m_i) { cout << "A(A)" << s.m_i << endl; }//拷贝构造
    ~A() { cout << "~A" << endl; }//析构函数
private:
    int m_i;
};
void fn(A s)
{
    cout << "fn" << endl;
}
A text()
{
    A tt(30);
    return tt;//第3种
    /*
     但返回值为类类型,从函数中先到一个临时空间,然后到主函数中,
     只有在从函数到临时空间时的旧的到新的,调用拷贝构造函数,
     但从临时空间到主函数时,编译器自动进行了优化,不调用拷贝构造函数
    */
}
int main()
{
    A a(10);
    A b(a);//第1种
    fn(b);//传参,第2种
    cout << "111" << endl;
    A s = text();
    cout << "222" << endl;
}

十三、new、delete和malloc、free的区别

new和 delete:运算符,new既能开辟空间,又能构造对象

                         new和delete可以重载

new int //开辟一个存放整数的存储空间
new int(100)//同上,并制定该整数的初始值是100
new char[100]//开辟一个存放字符的数组(100个字符)
new int[4][5]//开辟一个存放二维数组的空间,返回首元素地址
float *p=new float(3.14157)//开辟一个存放单精度的空间,并指向该数的初值3.14157
//指针p存放在栈中,但指向的空间在堆中,new出的空间位于堆中
int* p = new int.
delete p;
char* p = new char;
delete p;
int* p = new int[100];
delete []p;

malloc和 free:是开辟和释放空间库函数,malloc只开辟空间,不能构造对象

                        malloc和free不能重载

#include<stdio.h>
#include<stdlib.h>
void main()
{
    int* p;
    p = (int*)malloc(sizeof(int)*100);//分配100个int型字节的内存
    Student* p = (Student*)malloc(sizeof(Student));//开辟了一个Student类大小的空间
    //…………
    free(p)
}

总结:1.new和delete会自动调用对象的构造函数和析构函数,malloc和free不会

           2.new和delete是C++的运算符,并且可以进行重载;malloc和free是C/C++的库函                    数,不能进行重载

十四、static静态数据成员和static成员函数

不同对象如何共享数据?

每个对象都有自己的数据成员,不同对象的数据成员无法共享

全局函数:谁都可以访问,不安全

静态数据成员可以实现数据共享,该类的所有的对象都共享这块静态存储空间,它不属于某一                             个对象,被所有对象共享

静态数据成员存储在静态存储空间,生命周期与整个程序相同,即使某个对象消亡了,它依然存在

静态数据成员的初始化:

                 在类内定义,类外声明;

                 静态数据成员不能直接初始化                 错误:static int a = 5;

#include <iostream>
using namespace std;
class A
{
public:
    static int i;
};
int A::i = 9;
void main()
{
    A a;
    A b;
    cout << a.i <<endl;//9
    cout << b.i <<endl;//9
    b.i = 8;
    cout << a.i <<endl;//8
    cout << b.i <<endl;//8
}

静态成员函数

1.静态成员函数的作用是处理静态数据成员

2.静态成员函数只能访问static成员,包括数据成员和成员函数

3.非static成员函数既可以访问static数据成员,也可以访问非static数据成员。static成员函数只能访问static成员

4.静态成员函数不与任何对象绑定在一起,不包含this指针

1.静态成员函数的作用是处理静态数据成员

2.静态成员函数只能访问static成员,包括数据成员和成员函数

#include <iostream>
using namespace std;
class Box()
{
public:
    static int c;
    int a,b;
    Box(int x,int y)
    {
        a = x;
        b = y;
    }
    static void fun()
    {
        cout << c << endl;//不能调用a或者b,只能调用static
        cout << " static fun ---" <<endl;
    }
};
int Box::c = 8;
void main()
{
    Box box (2 ,3);
    box.fun ();
    Box:: fun();
}

3.非static成员函数既可以访问static数据成员,也可以访问非static数据成员。static成员函数只能访问static成员

#include <iostream>
using namesapce std;
class Box
{
  public:
        static int c;
        int a,b;
        Box(int x,int y)
        {
            a = x;
            b = y;
        }
        void fun()
        {
            cout << a << " " << c << endl;
        //fun是非静态数据成员函数,可以调用静态数据成员,也可以调用非静态数据成员
        }
};
int Box::int c = 8;
void main()
{
    Box box(2, 3);
    box.fun();//通过对象,引用名
}

4.静态成员函数不与任何对象绑定在一起,不包含this指针

十五、友元

友元:让一个函数可以访问私有的成员friend

1.一个普通函数作为类的友元,让函数可以访问私有数据成员

2.一个类的成员函数成为另一个类的成员函数
   A::fn(){}想要访问B的私有数据成员,让fn成为B的友元(fn的定义要在B之下,声明可以在前面)

3.友元类:一个类的成员函数可以作为另一个类的友元函数
    如果当前类的所有成员函数都要访问另一个类,让当前类成为另一个类的友元

友元的特点:

1.不具有对称性

2.不具有传递性

3.不具有继承性

1.一个普通函数作为类的友元,让函数可以访问私有数据成员

class A
{
public:
    A(int i=0,int j=0):m_i(i),m_j(j){}
    const int& GetI() { return m_i; }
    const int& GetJ() { return m_j; }
    friend void Add(A& a, A& b);
private:
    int m_i;
    int m_j;
};
void Add(A& a, A& b)
{
    int i = a.m_i + b.m_i;
    int j = a.m_j + b.m_j;
    cout << i << " " << j << endl;
}
void main()
{
    A a(10, 10);
    A b(20, 20);
    Add(a, b);
}

2.一个类的成员函数成为另一个类的成员函数

class B;//向前引用声明
class A
{
public:
    A(int i = 0) :m_i(i) {}
    friend void Add(A& a, B& b);//声明友元函数
private:
    int m_i;
};
class B
{
public:
    B(int j=0):m_j(j){}
    friend void Add(A& a, B& b);//声明友元函数
private:
    int m_j;
};
void Add(A& a, B& b)
{
    cout << a.m_i + b.m_j << endl;
}
void main()
{
    A a(10);
    B b(20);
    Add(a, b);
}

友元类:一个类的成员函数可以作为另一个类的友元函数

class B;
class A
{
public:
    A(int i=0):m_i(i){}
    void fn(B& b);//fn是A的成员函数
private:
    int m_i;
};
class B
{
public:
    B(int j = 0) :m_j(j) {}
    friend void A::fn(B& b);
private:
    int m_j;
};
void A::fn(B& b)
{
    cout << m_i + b.m_j << endl;
}
void main()
{
    A a(1);
    B b(4);
    a.fn(b);
}

十六、运算符重载

1.运算符重载,就是对已有的操作符重新进行定义,简化操作,让已有的运算符适应不同的数据类型;

2.运算符重载就是定义一个函数,在函数体内实现想要的功能,当用到改运算符是,会调用这个函数,运算符重载的本质就是函数重载;

3.不要更改运算符的含义,比如不要把 + 重载成减法(-)的含义

运算符重载

运算符重载能够重载为两种形式

1.重载成为成员函数

2.重载成为友元函数

区别值返回和引用返回:

值返回:不能作为左值,就必须用值返回,a+b不能作为左值,因为a+b存储在临时空间

                运算符组成的表达式只能放在=的右边

                +,值返回

                A operator+(A &b)

                {

                        return m_i+m_j;

                }

引用返回:可以作为左值,就可以用引用返回

如果能用引用返回,就不要用值返回

重载前置++(++a),后置++(a++)

class A
{
public:
    A(int i = 0):m_i(i){}
    A operator+(const A& s)
    {
        
        return (m_i + s.m_i);
    }
    void Print()
    {
        cout << "i = "<<m_i << endl;
    }
    friend A operator-(const A& a, const A& b);
    
    A operator++(int)//为了区别前++和后++,让它们构成重载,在后++里写一个int,
    {                    //不接受任何值,没有什么意思,就是做为区别
        return A(m_i++);
    }
    A& operator++()//前置++,使用,引用返回
    {
        ++m_i;
        return *this;
    }
private:
    int m_i;
};
A operator-(const A& a, const A& b)//重载成友元形式
{
    return A(a.m_i - b.m_i);
}
void main()
{
    A a(10);
    A b(20);
    A c;
    (a + b).Print();
    (b - a).Print();
    (a++).Print();
    (++a).Print();
    ++c = b;
    c.Print();
}

重载<<

cout是一个对象,ostream类的一个对象
cin是一个对象,istream类的一个对象

class A
{
public:
    A(int i = 0) :m_i(i) {}
    void Print()
    {
        cout << "i = " << m_i << endl;
    }
    A& operator=(const A& s)
    {
        if (this == &s)
            return *this;
        m_i = s.m_i;
        return *this;
    }
    friend ostream& operator<<(ostream& os, const A& a);
private:
    int m_i;
};
ostream& operator<<(ostream& os, const A& a)//<<输出的运算符不能重载为成员函数,只能重载为友元函数
//因为要写成成员函数,就要更改类,但我们不能改写ostream类
{
    os << a.m_i;
    return os;
}
int main()
{
    A a(10);
    A b(20);
    /*b = a;
    b.Print();*/
    (a = b).Print();
    cout << a << endl;
    return 0;
}

十七、this指针

this指针的作用:指向成员函数所作用的对象

非静态成员函数中可以直接使用this来代表指向该函数作用的对象的指针;

静态成员函数是共享的变量,不是属于某个对象的变量,没有this指针

十八、const指针

1.常量指针

const int* p = &a;

指针的指向可以改变,但指针指向的值不可改变

2.指针常量

int* const p = &a;

指针的指向不能改变,指针指向的值可以改变

3.const既能修饰指针,也可以修饰常量

const int* const p = &a;

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值