C++你可能不知道地方

c++中编译器替我们完成了许多事情,我们可能不知道,但也可能习以为常。下面详细介绍
 
一、初始化与初始赋值
首先说说类的初始化与初始赋值之前的区别,这也许里面可能有我们不知道的事情。  
其实类初始化与初始赋值还是有区别的。 
 1         class People{
 2         public:
 3                 People(std::string name,int age,int height);
 4         private:
 5                 std::string m_sName;
 6                 int m_iAge;
 7                 int m_iHeight;
 8         }
 9         //赋值
10         People::People(std::string name,int age,int height)
11         {
12                 m_sName=name;
13                 m_iAge=age;
14                 m_iHeight=height;
15         }
16         //初始化列表
17         People::People(std::string name,int age,int height)
18         :m_sName(name),m_iAge(age),m_iHeight(height)
19         {}
 C++规定,对象的成员变量初始化动作发生在进入构造函数本体之前。在构造函数内成员变量赋值都不是初始化,而是赋值。
 赋值时首先调用默认构造函数为m_sName,m_iAge,m_iHeight赋初始值,然后在立刻调用赋值操作符进行赋新值。
 成员初始列表是将各个成员变量实参都作为复制构造函数的实参。
 所以看出赋值相对于初始化,多了一步就是使用赋值操作符进行赋值。所以初始化的效率比赋值的效率高多了。但是对于内置类型,它们效率是一样的。
 
二、空类
  想想你如果声明一个空类,C++编译器会对它做什么呢?编译器就会为它声明一个复制构造函数,赋值操作符和一个析构函数,以及默认构造函数。所有这些函数都是public而且inline函数。
编译器写的赋值构造函数和赋值操作符,只是单纯地将来源对象的每个non-static变量拷贝到目标对象,具体是进行位拷贝。
     如果声明了一个构造函数,编译器是不会创建默认构造函数。
  如果不希望类支持拷贝构造函数与赋值操作符怎么办?不声明?按照上面说明编译器会自动帮你生成。那么可以将它们声明为private,这样阻止编译器自动生成拷贝构造函数(public)。private成功阻止他人使用,但是这并不安全。因为类成员函数以及友元函数还是可以调用private的拷贝构造函数和赋值操作符。
     如果只在private下声明拷贝函数和赋值操作符,在有人通过类成员函数去以及member函数去调用它,会获得一个连接错误。那么这里能不能将错误在编译的时候体现出来呢?这里只用将拷贝函数声明为private,并且不在自身,就可以办到了。显然继承一个拷贝函数和赋值操作符为private的基类就办到了,基类如下: 
1         class NonCopyable{
2         protected:
3                  NonCopyable (){}
4                 ~  NonCopyable (){}
5         private:
6              NonCopyable (const  NonCopyable &);
7              NonCopyable & operater=(const  NonCopyable &);
8         };  原因是类成员函数或者友元函数尝试拷贝对象,编译器便会尝试生成一个复制构造函数与赋值操作符,并会调用基类的对应函数,但是会被拒绝,因为基类这些函数是private。
 
3、++函数
  下面说说“*++"与"++*"中你不知道的事情,c++规定后缀形式自加函数有一个int类型参数,当函数被调用时,便其一传递一个0作为int参数的值传递给该函数,而前缀形式自己函数,类型参数没有要求,所以这样就能区分一个++函数是前缀形式与后缀形式了,具体代码如下: 
 1 class UPInt{
 2 public
 3     UPInt& operator++( ) ;                      //++ 前缀
 4     const UPInt operator++( int );            //++ 后缀
 5     UPInt& operator --( );                        // --  前缀
 6     const UPInt operator --( int )              // --  后缀
 7     UPInt& operator +=( int );                 //
 8     ...
 9 };
10 
11 UPInt & UPInt::operator++( )
12 {
13     *this += 1;
14     return *this;
15 }
16 
17 const UPInt UPInt :: operator++( int )
18 {
19     UPInt oldValue = *this;
20     ++(*this);
21     return oldValue;
22 }后缀函数使用返回参数类型const,是为了避免下面代码生效
1 UPInt i;
2 i++++;这个时候第一次调用++返回cosnt对象,并再次调用然后这个函数是non-const成员函数,所以const对象无法调用这个函数,那么i++++就无法生效了。
这里说说效率问题,我们可以看到后缀++函数建立一个临时对象以作为它返回值,这个临时对象经过构造并在最后被析构。而前缀++函数没有这样的临时变量,并且没有那样的操作。所以如果我们在程序中使用前缀++效率会更加高一些,没有了临时变量的构造与析构的动作。
 
4.虚析构函数
带有多态性质的base class应该声明一个virtual析构函数。
为什么这么说呢?看下面例子
        class base
        { ... }
        class derived:public base
        {... }
        
        base * p= new derived;
     假设这里基类的析构函数不是virtual,当使用完p指针,我们删除它的时候,想想会发生什么,因为基类的析构函数是non-virtual所以不会发生多态直接调用基类析构函数,仅仅删除继承类中基类那部分内容,那么继承类对象其他内存没有被销毁,从而资源泄漏。
    如果将其声明为virtual,那么就会发生多态,调用的是指向继承类的指针,那么就会销毁的是整个继承类象。
 
5.传递方式用引用
 缺省情况下c++以值传递方式传递对象至函数。函数参数都是以实际实参的复件为初值,而调用端所获得的是函数返回值的一个附件。这些复件都是由拷贝构造函数产出。看如下例子 
 1         class Person{
 2         public:
 3             Person();
 4             virtual ~Person();
 5             ...
 6         private:
 7             std::string name;
 8             std::string address;
 9         }
10         
11         class Student:public Person{
12         public:
13             Student();
14             ~Student();
15             ...
16         private:
17             std::string schoolName;
18             std::string schoolAddress;
19         }; 那么如果有一个函数验证是否为学生 1   bool validateStudent(Student s);
2   Student plato;
3   bool platoIsOK=validateStudent(plato);
  分析这3行代码,编译器到底做了什么?首先调用Student的copy构造函数,然后以plato为蓝本将s初始化,当validateStudent返回被销毁,所以成本为"一次Student copy构造函数调用,加上一次Student析构函数调用"。
      Student对象内部有两个string对象,所以构造了两个string对象。Student继承自Person对象,里面又有两个string对象。所以by value方式传递一个Student对象,总体成本是"六次构造函数和六次析构函数"!

      以by reference方式传递参数也可避免对象切割问题。当一个derived class对象以by value方式传递并被视为一个base class对象,base class的copy构造函数会被调用,造成像derived class对象全被切割掉了,仅仅留下base class对象。看如下代码通过传递引用参数完成多态 
        class Window{
        public:
            ...
            std::string name() const;
            virtual void display() const;
        };
        class WindowWithScrollBars:public Window{
        public:
            ...
            virtual void display() const;
        };
        
        //传入Windos类型,调用其display函数
        //传入WindowWithScrollBars类型,调用其display函数
        //体现多态
        void printNameAndDispaly(const Window& w)
        {
            std::cout<<w.name();
            w.display();
        }    窥视c++编译器的底层,reference往往以指针实现出来,因此pass by reference真正传递的是指针。如果对象属于内置型,pass by value往往比pass by reference 效率高些。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值