C++review___1

1,静态成员函数中能访问非静态成员(包括属性和函数)吗?
                不能直接访问。
                因为所以对非静态成员的访问都是通过this指针,而静态成员函数中没有this

2,静态成员函数中能访问静态成员(包括属性和函数)吗?
                可以 
                静态成员属于类,不属于对象,不需要通过 对象去访问
3,静态成员函数可以是常函数吗?
                不可以
                常函数的 const是限定  *this 的 (不能通过 this去修改它指向的对象),但是静态成员函数中没有this

        和静态成员变量一样,也可以通过类名去访问静态成员函数
            对象名.函数名(参数);
            或者
            类名::函数名(参数);

4,为什么C++中不用C语言的malloc?

        解:c++中也可以使用 malloc/free ,但是有缺陷:
               当你 用malloc 分配一个 class类型的对象时,不会自动调用构造函数
                且用 free 释放一个 堆区 的 class类型的对象时,不会自动调用析构函数

                所以建议 c++不要使用 malloc/free

    c++专门提供了两个运算符 new / delete:
            new 是用来分配堆区空间的
            delete 是用来释放堆区空间的

new 一个对象 
            类型 * 指针名 = new 类型;//没有初始化
            或者
            类型 * 指针名 = new 类型(初始值);//初始化 

delete 一个对象
            delete 指针变量名;


new 一组对象
            类型 * 指针名 = new 类型[元素个数];
            或者
            类型 * 指针名 = new 类型[元素个数]{初始值,初始值.....};
                初始值的个数可以少于等于元素个数

delete 一组对象
            delete [] 指针变量名;//不加 [] 不会报错,但是只会释放一个对象。

5,引用

(1)引用作为函数参数:调用函数传参时,引用传递比值传递的效率高(就自定义类型而言),因为引用传递不需要为形参分配空间,也不需要调用构造类型的构造函数。

(2)引用作为返回值:这种情况下,函数调用表达式可以作为左值。

(3)常引用:用const修饰的引用,常引用不能修改。一般用于函数参数,不能通过形参去修改实参的值,限制它只能进行读访问。

(4)引用的底层实现:可能是一个指针

6,默认的拷贝构造函数

//默认生成的拷贝构造函数长这样,举例:
            Student(const Student &other)
            {
                this->m_id = other.m_id;
                this->m_age = other.m_age;
                this->m_name = other.m_name;
                this->m_score = other.m_score;
                cout << "student拷贝构造函数"<< endl;
            }

        深拷贝和浅拷贝:编译器默认生成的那种逐个成员赋值的形式脚浅拷贝,浅拷贝有局限性:当类中有指针类型成员时,该指针应该指向一个堆区空间。此时用浅拷贝去初始化一个新对象的话,那么新对象和原对象的指针成员都会指向同一堆区空间,当一个对象对该内存空间进行修改时,另一个对象也被动修改了,当对象销毁时,都是调用的析构函数对同一堆区空间进行销毁,会造成重复delete。所以,当类的成员有指针时,需要自己实现拷贝构造函数,也就是深拷贝。

        

7,右值引用和移动构造

        (1)c++11标准增加了右值引用的概念
                前面讲的引用 ->左值引用, c++11之前 只能用 常量引用

        (2)右值引用语法格式:类型 && 右值引用名 = 右值对象;//&&是右值引用声明符

        (3)右值对象:如,常量,临时对象,快要销毁的对象.....

        (4)右值引用主要是用于移动构造函数。

        (5)前面讲的拷贝构造的例子,有一种情况:
                    原有对象将要销毁,那么原有对象的资源将要释放,而新对象又需要重新分配资源。
                    为什么不直接把原有对象的资源给新对象?

                    于是,右值引用就派上了用场。

        (6)移动构造函数语法:移动构造函数只有一个显式参数:本类对象的右值引用

                函数名/类名(类名 &&other)
                {}

      8,运算符重载

         (1)重载为静态成员函数会报错,因为静态成员函数没有this指针,不能访问非静态成员。所以,运算符重载为非静态成员函数时,要比重载为友元函数少一个显示参数(有一个隐形的this)调用运算符重载函数时,运算符左边的操作数地址传递给this,右边的操作数传递给唯一的那个参数。s1+s2=>s1.operator+(s2)

        对于既可以重载为成员函数又可以重载为友元函数的运算符而言,只需要实现一个版本即可,否则会造成二义性,报错。

        (2)重载时,返回值是返回*this还是引用?

        解:可以返回引用,因为返回的对象是*this,该对象的生存期并不是随代码块结束的,并且能返回引用就返回引用,因为返回引用比返回值的效率要高些。

       (3)C++中,重载输出运算符,用的是C++中用于标准输出的类ostream,它有唯一的对象cout,ostream类中重载了很多次<<运算符。

        如:

//int类型数据输出
ostream& ostream::operator<<(int data)
{
    printf("%d",data);
}

//double类型数据输出
ostream& ostream::operator<<(double data)
{
    printf("%lf",data);
}

        (4)为啥都是返回ostream的引用?

        解:因为ostream的类只允许创建一个对象,如果返回值,就会创建临时对象,会失败。

        (5)为啥不能是void?

        解:因为需要支持连续输出。

        (6)现在需要重载为自定义类型Mystring,该怎么重载?      

                A 重载为 ostream 类的成员函数 

                B 重载为 ostream 类的友元函数

               C 重载为 Mystring的成员函数

               D 重载为 Mystring的友元函数

        解:AB都不可取,因为需要修改ostream类中的代码,而它是标准库中的代码,不能修改。C的话意味着两个参数都是Mystring对象,不对。因为正确的参数应该是第一个是Mystring对象,第二个是ostream对象=>        s1 << out        ,但是s1 << cout << endl;  //太奇怪了,不符合大家的习惯;所以第四个才是对的,也就是ostream& operator<<(ostream& out,const Mystring &s);

9,C++11标准中的关键字:delete,default,explicit

        编译器在没有自己实现函数时会自动为类生成一些函数,当我们不需要某个自动默认生成的函数时就可以用delete;反之,希望自动生成时,就用default;

        explicit禁止隐式转换。

        如:何为隐式转换呢?
            Mystring s2 = "world";
            = 左边是 Mystring 类型
            = 右边是 const char * 类型
            两个类型不一致,怎么能赋值呢?

          所以,先用 "world" 转换为一个 Mystring的临时对象,然后用这个临时对象初始化 s2
            所以建议尽量不要向上面的写法,而应该按照下面的写法:
            Mystring s2("world");

        如果不想要允许进行这样的转换,就在构造函数前加上 explicit关键字即可

10,赋值运算符

        当用一个已经存在的对象给另一个已经存在的对象赋值的时候,会调用赋值运算符重载函数(拷贝赋值函数),一定要注意和初始化的区别(初始化调用拷贝构造)。如果程序员没有自己实现拷贝赋值函数,编译器会自动生成一个(逐个成员赋值,浅拷贝)如果需要执行深拷贝,就得自己写。

        注意:
        拷贝赋值函数只能重载为成员函数,如果你重载为友元函数,编译器会为你自动生成一个拷贝赋值的成员函数就会产生二义性 ,报错。

11,移动赋值:用一个右值对象给一个已经存在的对象赋值时会调用移动赋值函数

12,小总结:

        默认情况下,一个空类会自动生成:
                                        原型                                            调用时机
        默认构造函数        Mystring();                                     构造对象时不传参数
        析构函数              ~Mystring();                                    对象被销毁
        拷贝构造函数        Mystring(const Mystring &);           用一个已有的对象初始化一个新对象
        拷贝赋值函数        Mystring& operator=(const Mystring &);      用一个已有的对象为另个已有对象赋值
        c++11还有两个
        移动构造函数        Mystring(Mystring &&);                      用一个已有的右值对象初始化一个新对象
        移动赋值函数        Mystring& operator=(Mystring &&);           用一个已有的右值对象为另一个已有对象赋值

13,下标运算符[] :必须要重载为成员函数

        双目运算符:要两个操作,第一个参数是某个对象
                             第二个参数是整数:[0,n-1]

        以 Mystring为例:
        char&  Mystring::operator[](int index)
        {
            return str[index];
        }

14,常对象,用const修饰的对象

        常对象只能调用常函数,不能调用非 常函数 

        以Mystring为例
        如果有个常对象 s1, 不能调用非常函数, cout << s1[1] << endl;报错 
        但是逻辑上是有这个需求的,允许常对象对其成员进行读访问
        怎么解决?
        给 operator[]  函数加上 const?
        那么 s1[1] = 'X' 这句话也不会报错了,-》那么就对常对象进行修改了,也有问题!!!!
        重载两次:
            char& operator[](int index);            //给普通对象调用,返回引用,可读可写
            const char& operator[](int index)const; //给常对象调用,返回常引用,只读不可写

15, ++      --      运算符

a++         ++a 
    对于a本身而言两者并没有区别,区别在于表达式的值不一样, a++ 这个表达式的值是 a自增之前的值,
    ++a这个表达式的值是 a自增之后的值。

    ++ 是单目运算符,有 前++ 和 后++之分,重载一次显然满足不了要求,-》需要重载两次
    后++ 要提供一个 “占位形参”,只是起到占位置的作用(用于重载),函数内部并不会用到它,调用函数时,
    也不需要为这个形参传递实参
    operator++(int) 
    operator++()

16,函数调用运算符重载  ()


    如果一个类中重载了 () ,那么这个类的对象 被称之为 函数对象/仿函数
    -》因为他的行为类似函数,可以调用。

例子: 

 class Less
        {
        public:
            Less(int data =0)
            {
                m_data = data;
            }
            bool operator()(int x)
            {
                if(x < m_data)
                    return true;
                else
                    return false;
            }
        
        private:
            int m_data;
        };

        int main()
        {
            Less l(80);//因为 Less重载了() ,所以 l就是函数对象/仿函数 

            bool b = l(90);
            if(b)
            {
                cout << "l >= 90" <<endl;
            }
            else
            {
                cout << "l < 90" <<endl;
            }
       }

 函数重载只能给已有的运算符赋予新的功能,不能创造一个新的运算符 ,比如: @ # $ 
        大部分已有的运算符可以被重载,但是也有一些运算符是不能被重载的:
                .       用于访问对象中的成员        
                ::      作用域运算符
                ?:      问号运算符
                sizeof  
                .*   

17, new/delete 和 malloc/free 的区别

> new 和 malloc都是在堆中分配空间
> new 是堆中创建一个对象,并返回其指针。“类型”,“调用该类型的构造函数”
> malloc仅在堆中分配一块空间,并返回其地址。 “无类型”
> new的时候,会调用对象的构造函数;malloc不会
> new分配的对象,销毁时必须用delete
> malloc分配的空间,释放时必须用free
> new/delete是运算符, malloc/free是一个库函数

18,继承

 一个派生类对象中包含两个部分
        1,从基类继承过来的部分 (基类子对象)
        2,派生类自己新增的部分

    由于派生类对象中包含了基类子对象,那么我们能不能把派生类对象当做基类对象使用呢?
    可以的
    前提是继承方式是公有继承

    反过来不行的:也就是基类对象不能当做派生类对象使用

    公有继承的派生类对象可以当做基类对象使用,具体体现在:
        可以使用派生类对象初始化基类对象

        可以使用派生类给基类对象赋值

        基类的引用可以绑定派生类对象
            基类 &引用名 = 派生类对象;

        基类的指针可以指向派生类对象
            基类 *指针名 = &派生类对象;

19,类的成员函数的地址空间由类的对象共享,而它的成员却是每个对象一人一份。所以在计算class对象所占内存空间大小时,只需要考虑它的成员,不需要考虑它的成员函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值