zoukankan      html  css  js  c++  java
  • 2.名字空间和构造函数

    一.名字空间
     C语言中的名字空间:全局,局部,块
     C++认为:全局空间用起来非常方便,但是如果把太多的东西放在全局空间中,会造成命名冲突,所以C++引用了这样一种机制,把全局空间在进行划分
     名字空间:把全局的命名空间进一步分割。可以创建出一个个独立的命名空间,防止相互之间冲突。
     1.定义名字空间:
     namespace name
     {
      变量;
      函数;
      结构,类;
     }
     2.名字空间的合并:
      a.同名的名字空间会自动合并
      b.在同一个命名空间中的标识符必须是唯一的
     3.名字空间的定义和声明分开
      namespace n1 //在名字空间中声明函数
      {
       void func(void);
      }
      void n1::func(void) //在名字空间外定义函数
      {
      }
     4.然后使用名字空间中的内容
      a.域限定符(::),直接使用,名字空间::标识符,这样的好处是绝对不会冲突会麻烦一点
      b.using namespace 名字空间名;
      功能:把名字空间中的标识符对之后的代码全部公开
      c.using 名字空间::标识符,标识此标识符对后面的代码公开
     5.名字空间的嵌套
      名字空间可以嵌套,但是用的时候要逐层解析
      namespace n1
      {
       int num = 10;
       namespace n2
       {
        int num = 20;
        namespace n3
        {
         int num = 30;
        }
       }
      }
      n1::n2::n3::num == 30;
      n1::n2::num == 20;
      n1::num = 10;
      using namespace n1::n2;
     6.全局空间归属到匿名空间
      在全局空间定义的标识符都属于这个匿名空间,匿名空间默认对所有的位置都开放。
      如果函数中有同名的标识符把匿名空间中的屏蔽了,可以使用空的域限定符表示它。
      ps:不同命名空间的同名函数不构成重载,同一作用域下的同名函数叫重载。
    二.class
     1.C++中的class和struct,几乎没有任何区别一样,是一种复合数据类型。
     2.里面可以有变量用来表达属性,函数用来表示行为。
     3.C++中的class和struct,几乎没有任何区别。
     4.struct 中的默认访问属性是public,class中的默认访问属性是private
     5.C++中默认使用class,和C以示区分
    三.class的构造函数
     1、在创建对象时自动调用的函数,在整个对象的生命周期中一定会被调用一次,且只能被调用一次。
     2、在构造函数中负责对成员变量的初始化、分配资源、设置对象的初始状态。
     3.构造函数可以有很多版本,这些不同的版本之间会构成重载,创建对象时,方法不同,所给参数不同,会调用相应的构造函数
     如果调用的构造函数不存在,可能会造成编译错误
      // 无参构造
      Student stu <=> Student* stup = new Student;
      Student stu(参数列表) <=> Student* stup = new Student(参数列表);
     4.如果类中没有定义构造函数,编译器会自动生成一个无参构造函数
      一旦定义了其他版本的构造函数,无参构造函数就不会再生成了,因此为了防止无参方式创建对象出错,再定构造函数时,至少要实现两个
     5.无参构造未必无参,在C++中函数可以有默认参数,如果有参构造全部设置了默认参数,就会和无参构造有冲突,它们两个只能存在一个。
     6.所谓的"编译器生成的某某函数"
      "编译器生成的某某函数",不是真正意义上的函数,编译器作为指令的生成者,只要生成具有某些函数功能的指令即可,没有必要生成高级语言意义上的函数
     7.什么时候调用无参构造函数
      a.Student stu <=> Student *stup = new Student;
      b.创建对象数组,每个对象都会调用一次无参构造
      c.如果类A中有成员是类B,当执行完类A的构造函数前,就会自动调用类B的无参构造
      d.在类A中,如何调用类B的有参构造
       类A(参数列表):成员B(参数列表)
     8.类型转换构造函数
      用一种数据给对象初始化,默认会自动调用构造函数,达到类型转换的效果
      这种方式虽然使用方便,但是会容忍一定的错误存在,如果想让代码的检查更为严格,可以使用explicit关键字禁止隐式转换的方式调用构造函数
     9.也可以实现自动类型转换构造函数(默认)
    练习:
    1、写一个Date类,有属性:年、月、日,实现其各种构造函数。
    2、写一个Timer类(属性定义为私有,方法定义为公开)
         有一个属性 usigned int second;//second记录定时器的秒数
         有一个属性 Action action;//定时器响应动作
         typedef bool (*Action)(void *);
         有一个方法setAction(Action a),当调用begin()方法之后,second秒之后自动调用a函数。
    四、拷贝构造函数

     1.是一种特殊的构造函数,就是用一个已有的对象去构造其同类的副本对象,即对象克隆 
     class 类名
     {
      类名(类名 &that)
      {
       //对成员挨个赋值
      }
     }
     2.编译器会默认生成一个拷贝构造函数
      编译器生成的拷贝构造函数默认会逐字节复制类中的每一个成员。
      如果类A中有类B成员,会在类A的拷贝构造中自动调用类B的拷贝构造
     3.程序员可以自定义拷贝构造来取代默认的拷贝构造
      a.拷贝构造只能有一个,不能重载
      b.一旦程序员自定义了拷贝构造函数,编译器就不再生成了
      c.在自定义的拷贝构造中能通过编码来实现成员的复制
     4.一般情况下,编译器生成的拷贝构造函数完全够用,不要轻易自己定义拷贝构造
     5.什么情况下调用拷贝构造函数:
      a.对象给对象赋值
      b.用对象给函数传参
      c.用对象当作返回值
    .初始化列表
     1.是一种成员的初始化方式,在构造的函数的大括号体前使用小括号对类的成员进行初始化
     class 类名
     {
      类名(参数列表):成员(参数),成员(参数)...
      {
      }
     }
     a.参数列表可以解决构造函数的参数于成员重名
     b.参数列表会优先于构造函数先执行
     2.如果有成员是类,可以使用{}进行初始化
     3.如果有成员是类,可以在初始化列表中显示的调用构造函数
     4.如果成员中有const或引用成员,必须使用初始化列表
     5.类成员的构造顺序与初始化列表无关,而是与成员定义的顺序有关
    五.this指针
     1.相同类型的对象各自拥有独立的成员实例,彼此共享一段代码段
     2.为了让成员函数知道是哪个对象在调用,并准确访问到对象的成员,编译器会自动为每一个成员函数添加一个看不到的参数,这个参数就是指向对象的指针(this)
     3.基本上类中的所有成员函数都有this指针,包括,拷贝,构造,析构,拷贝构造等。
      只是构造函数中this指向的是正在被创建的对象。
     4.this指针默认情况下都是隐藏的(在成员函数访问成员变量时自动就加上了),但是也可以显示使用
     5.什么情况使用this
      a.区分成员变量与参数
      b.把对象当作返回值与其他对象进行交互
    六.常对象与常函数
     1.创建对象的时候前面加上const关键字,这个对象就不可再修改,就有了常属性,就意味着整个对象中的所有东西都不能改
     2.常对象不能调用普通成员函数,调用成员函数就相当于把对象的this指针给了它,就会有被修改的风险
     3.函数体前加const的叫常函数,常对象只能调用常函数,普通对象也能调用常函数
      常函数就相当于对this指针添加了const属性
     4.常函数与‘非’常函数会形成重载不会冲突
     5.如果有成员确实需要修改,但是这个对象又被const修饰,可以对成员添加关键字mutable,这样即使是常对象调用了常函数依然可以修改成员
    七.析构函数
     1.当对象被销毁时自动调用的函数叫析构函数,对象的整个生命周期只能被调用一次,它是对象被销毁前的最后一个动作。
      class 类名
      {
       //不能重载只能有一个
       //不可以有返回值,不可以有参数
       ~类名(void)
       {
       }
      }
     2.编译器会默认产生一个析构函数,默认析构函数负责销毁看的到的类成员,如果有成员是类,会自动调用其析构函数,且类成员的析构过程和创建过程是相反的
     3.析构函数虽然不能重载,但可以自定义,自定义了析构函数,默认的析构函数就不会生成了。
     4.当类中有析构函数看不到的资源时(new/delete),有需要还原的设置(打开的文件关闭/把获取到的数据保存),这就需要自定义析构函数
     练习:实现一个学生类,实现其构造,拷贝构造函数,当对象被销毁时,自动写入stu.txt文件中。
    八.赋值构造
     1.一个对象给另一个对象赋值的时候调用的函数
      Student stu2 = stu1;//拷贝构造
      stu2 = stu1;//赋值构造
      void func(Student stu); // 拷贝构造
      func(stu1);
     2.赋值构造的格式
      void operator = (Student &that)
      {
      }
      //可以与其他对象进行交互
      Student &operator = (Student &that)
      {
      }
     3.编译器会默认生成赋值构造,它的功能与拷贝构造的功能一样,把对象A完全拷贝给对象B
     4.赋值构造与拷贝构造区别
      赋值构造是在两个对象都已经创建好的情况下,完成的两者间的赋值
      而拷贝构造是使用对象A去创建出对象B(对象B还不存在)
      如果对象中有常成员,拷贝可以调用,赋值不可以。
     5.一般情况下,默认的赋值构造基本够用,除非有成员是指针指向了额外的内存空间,这种情况下才需要自定义拷贝构造和赋值构造。
     6.自定义赋值构造
      a.确定赋值构造的格式
      b.防止自赋值
      c.释放旧资源
      d.分配新的资源
      e.拷贝新内容
      f.代码复用(调用拷贝构造函数)
    九.静态成员与静态函数
     1.类成员被static修饰后,就存储在bss段(由编译器存放,大小固定的),在程序中,动态的创建对象时,静态成员就无法创建,所有的类对象共享一个静态成员。
     2.静态成员只能在类中声明,不能在类中定义(必须在类外定义)
      类型 类名::成员名;
     3.静态成员就是声明在类中的全局变量,在任何位置都可以使用类名::静态成员名进行访问
     4.静态成员函数,类中的成员函数被static修饰了以后就变成了静态成员函数,所有的对象共享一份静态成员函数
      静态成员函数不会传递this指针,也就不能访问成员变量
     5.静态成员函数不会传递指针,也就不能访问成员变量
      不通过对象也能调用静态成员函数
      类名::静态成员函数(参数)
    十.单例模式
     1、只能创建出一个对象的类,这种类就叫作单例类,这种模式就叫作单例模式。
     2、为什么需要单例模式,是为了提高安全性和稳定性的技巧。
      只允许存在唯一对象实例
      单例模式的商业应用:
       网站计数器
       日志管理系统
       连接池、线程池、内存池
     3、获取对象实例的专门方法
      a、全局变量的定义不受控制,能防君子不能防小人
      b、专门方法是类的一部分,"我是类型我做主",
      借助类禁止在外部创建对象,仅在类内部提供获取对象的接口。
     4、如何实现单例模式
      a、禁止在类外部创建实例,私有所有的构造函数 private
      b、类自己维护其唯一实例,
       静态成员变量 static 类名 instance;
       静态成员指针 static 类名* instance;
      c、提供访问该实例的方法,静态成员函数getInstance()
     5、饿汉单例模式
      不管是否需要对象都已经创建好了。
      优点:效率高、速度快、稳定。
      缺点:浪费资源,不管需不需要对象都已经创建好;
     6、懒汉单例模式
      当首次使用获取对象时才会真正创建出对象。
      优点:节约资源
      缺点:效率低,速度慢,不安全(多线程情况下)。

    饿汉模式:
    #include <iostream>
    
    using namespace std;
    
    class Singleton
    {
    	static Singleton intance;
    	Singleton()
    	{
    
    	}
    	Singleton(Singleton& that)
    	{
    
    	}
    	void operator = (Singleton& that)
    	{
    
    	}
    public:
    	static Singleton& getIntance(void)
    	{
    		return intance;
    	}
    };
    
    Singleton Singleton::intance;
    
    int main()
    {
    	Singleton& s = Singleton::getIntance();
    	cout << &s << endl;
    	Singleton& s1 = Singleton::getIntance();
    	cout << &s1 << endl;
    }
    
    懒汉模式:
    #include <iostream>
    
    using namespace std;
    
    class Singleton
    {
    	static Singleton* intance;
    	Singleton()
    	{
    
    	}
    	Singleton(Singleton& that)
    	{
    
    	}
    	void operator = (Singleton& that)
    	{
    
    	}
    public:
    	static Singleton& getIntance(void)
    	{
    		if(NULL == intance)
    		{
    			intance = new Singleton;
    		}
    		return *intance;
    	}
    	~ Singleton(void)
    	{
    		delete intance;
    	}
    };
    
    Singleton* Singleton::intance = NULL;
    
    int main()
    {
    	Singleton& s = Singleton::getIntance();
    	cout << &s << endl;
    	Singleton& s1 = Singleton::getIntance();
    	cout << &s1 << endl;
    }
    
  • 相关阅读:
    图片加载库Glide
    Home键和back键下 Activity的生命周期变化
    Fragment重叠问题
    Fragment与Activiy之间的交互
    android事件拦截处理机制详解 .--------转
    实现手机QQ的抖动效果
    点评点赞功能的基本实现------个人观点
    自定义侧滑菜单
    检查设备剩余内存
    StringByAppendingPathComponent和stringByAppendingString的区别
  • 原文地址:https://www.cnblogs.com/LyndonMario/p/9445152.html
Copyright © 2011-2022 走看看