C++的基本概念三

一、拷贝构造函数
    1、是一种特殊的构造函数,就是用一个已有的对象去构造其同类的副本对象,即对象克隆。
    class 类名
    {
        类名(类名& that)
        {
            对类成员挨个赋值
            ...
        }
    }
    
    练习:定义一个学生类,实现它的拷贝构造函数。
        Student stu2 = stu1; // 调用拷贝构造
        stu2 = stu1; // 调用赋值构造
    2、编译器会默认生成一个拷贝构造函数
        编译生成的拷贝构造函数默认会逐字节复制类中的每一个成员。
        如果在类A中有类B成员,会在类A的拷贝构造中自动调用类B的拷贝构造。
    3、程序员可以自定义拷贝构造来取代默认的拷贝构造。
        a、拷贝构造只能有一个,不能重载。
        b、一旦程序员自定义的拷贝构造,编译器就不再生成。
        c、在自定义的拷贝构造中能通过编码来实现成员的复制。
    
    4、一般情况下编译器生成的拷贝构造完全够用,不要轻易自定义构造。

    5、什么情况下调用拷贝构造:
        a、对象与对象赋值
        b、用对象与函数传参
        c、用对象当作返回值

    练习:已知有一个学生类
    class Student
    {
        char* name;
        char sex;
        short age;
    };
    Student stu1;
    Student stu2 = stu1;
    stu.name = new char[20];
    
    此类在使用过程中偶尔会发生段错误、数据丢失、内存泄漏,如何改进此类。


二、初始化列表
    1、是一种成员的初始化方式,在构造函数的大括号体前使用小括号对类的成员进行初始化的一种方式。
    class 类名
    {
        类名(参数列表):成员1(参数1),成员2(参数2)
        {
            
        }
    }
        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指针添加了cosnt属性。
    4、常函数与'非'常函数会形成重载不会冲突。
    5、如果有成员确实需要修改,它又需要被const修饰,可以对成员添加一个关键字mutable,这样即使常对象调用了常函数依然可以修改成员。

五、析构函数
    1、当对象被销毁时自动调用的函数叫析构函数,对象的整个生命周期中只能被调用一次,它是对象被销毁前的最后一个执行的动作。
    class 类名
    {
        // 不能重载、只能有一个
        // 没有返回值、没有参数    
        ~类名(void)
        {
            
        }
    }

    2、编译器会默认产生一个析构函数,默认析构函数负责销毁能看的到的成员,如果有成员是类,会自动调用成员的析构函数,成员的析构过程与构行过程相反。

    3、析构函数虽然不能与重载,但可以自定义,有自定义析构函数默认析构就不会生成。

    4、当类中有析构造函数看不到的资源时(new/malloc)、有需要还愿的设置时(把打开的文件关闭/把获取到的数据保存),这时就需要自定义析构函数。

    练习:实现一个学生类,实现其构造、拷贝构造函数,当学生对象被销毁时自动写谁的stu.txt文件中,要保证常对象也能写入。

六、赋值构造
    1、赋值构造就是一个对象给另一个对象赋值的时候调用的函数。
        stu2 = stu1; // 赋值构造

        Student stu2 = stu1;// 拷贝构造

        void func(Student stu);// 拷贝构造
        func(stu1);

        Student func(void) // 拷贝构造
        {
            return *this;
        }
        Student stu = func()
    2、赋值构造函数的格式
    void operator = (Student& that)
    {

    }

    // 可以与其它对象进行交互
    Student& operator = (Student& that)
    {

    }

    3、编译器会默认生赋值构造,它的功能与拷贝构造的功能一样,把对象A完全拷贝给对象B。

    4、赋值构造也拷贝构造的区别
      拷贝构造:使用对象A去创建出对象B(调用时对象B还末生成)。
      赋值构造:对象A与对象B都已经构造完成,此时B = A;
      如果对象中有常成员拷贝构造可以成功调用,但赋值构造不行。
    5、一般情况下默认的赋值构造基本够用的,除非有成员是指针,指向了额外的内存空间,这种情况下才需要自定义拷贝构造、赋值构造。
    6、自定义赋值构造
        a、确定赋值构造的格式
        b、防止自赋值
            int num = 1;
            num = num;
        c、释放旧资源
        d、分配新的资源
        e、拷贝新内容
        //f、代码利用(显式调用拷贝构造)
    
七、静态成员与静态成员函数
    1、类成员被static修饰后,就会存储在bss段(此段是由编译存放的而且大小固定),在程序中动态的创建对象时它的静态成员就无法创建,所有的类对象共享一个静态成员。
    2、静态成员只能在类中声明不能类中定义(必须在类外定义)。
        类型 类名::静态成员名;
    3、静态成员就是声明在类中的全局变量,在任何位置都可以使用
        类名::静态成员名进行访问。
    4、静态成员函数,类中的成员函数被static修饰后就变成了静态成员函数,所有对象共享一份静态成员函数。
    5、静态成员函数不会传递this指针,也就不能访问成员变量。
        不通过对象也能调用静态成员函数
        类名::静态成员函数(参数)
    
八、单例模式
    1、只能创建出一个对象的类,这种类就叫作单例类,这种模式就叫作单例模式。
    2、为什么需要单例模式,是为了提高安全性和稳定性的技巧。
        只允许存在唯一对象实例
        单例模式的商业应用:
            网站计数器
            日志管理系统
            连接池、线程池、内存池
    3、获取对象实例的专门方法
        a、全局变量的定义不受控制,能防君子不能防小人
        b、专门方法是类的一部分,"我是类型我做主",
        借助类禁止在外部创建对象,仅在类内部提供获取对象的接口。
    4、如何实现单例模式
        a、禁止在类外部创建实例,私有所有的构造函数 private
        b、类自己维护其唯一实例,
            静态成员变量 static 类名 instance;
            静态成员指针 static 类名* instance;
        c、提供访问该实例的方法,静态成员函数getInstance() 

    5、饿汉单例模式
        不管是否需要对象都已经创建好了。
        优点:效率高、速度快、稳定。
        缺点:浪费资源,不管需不需要对象都已经创建好;

    6、懒汉单例模式
        当首次使用获取对象时才会真正创建出对象。
        优点:节约资源
        缺点:效率低,速度慢,不安全(多线程情况下)。

总结:

  • 拷贝构造函数:当你定义一个类的实例并用另一个同类实例对其进行初始化时,程序会自动调用该函数,起到对该对象初始化功能。要能够区分拷贝构造函数(初始化)和赋值构造函数(赋值)。还要注意拷贝构造函数的创建格式,以及调用时机。        拷贝构造函数不可以重载,当可以自定义,而且若用户未定义,编译器会自动生成。     拷贝构造函数的三个使用时机(对象给对象初始化,使用对象给函数传参,将对象作为返回值)
    class Student
    {
    	char* name;
    	char sex;
    	short age;
    public:
    	//构造函数
    	Student(const char* _name,char _sex,short _age)
    	{
    		name = new char[strlen(_name)+1];
    		strcpy(name,_name);
    		sex = _sex;
    		age = _age;
    		cout << "----1----" << endl;
    	}
    	//赋值构造函数
    	Student(Student & that)
    	{
    		name = new char[strlen(that.name)+1];
    		strcpy(name,that.name);
    		sex = that.sex;
    		age = that.age;
    		cout << "----2----" << endl;
    	}
    	//普通成员函数
    	void show(void)
    	{
    		cout << name << " " << sex << " " << age << endl;
    	}
    	//析构函数
    	~Student(void)
    	{
    		delete [] name;
    		cout << "----3----" << endl;
    	}
    };

    而且,若对象在定义时调用拷贝构造函数,就不会调用构造函数,即拷贝构造函数内容至少要多与构造函数。(一个类的对象在定义时要么调用构造函数,要么调用赋值构造函数)      构造函数的参数必须得有一个同类的引用变量,而且其它参数要么没有,要么必须设置默认值(参考:https://blog.csdn.net/lwbeyond/article/details/6202256)

  • 初始化列表:在类的成员函数定义时,在函数体前函数参数列表后对函数参数进行初始化(就是一种初始化方式)。用途(解决参数列表名与成员变量名重名的问题,解决const成员变量初始化问题)

    class Dog
    {
    	char name[20];
    	char type[20];
    	const char sex;
    	short age;
    	const char* obj[12];
    public:
        //使用初始化列表,包括const变量、同名、数组初始化
    	Dog(const char* name,const char* type,char sex,char age,
    	const char* obj1,const char* obj2,const char* obj3)
    	:sex(sex),age(age),obj{obj1,obj2,obj3}
    	{
    		strcpy(this->name,name);
    		strcpy(this->type,type);
    		cout << name << "dog is coming !" << endl;
    		cout << obj[0] << obj[1] << obj[2] << endl;
    	}
    };

    重点:当你定义一个类的时候,程序会先实现类内的所有【定义】,然后再执行【初始化列表】,最后执行【构造函数】

  • this指针:用来指向特定的类的对象。所以,当调用该对象的成员函数时,程序会自动隐式将this指针传入成员函数,以保证成员函数知道调用对象。  但有时我们并不希望传入this指针进入某个成员函数(signal()),就可以在该成员函数前添加static关键字修饰,此时该函数被定为静态成员函数。  同时,this指针可以在成员函数内隐式调用,也可以显式调用。    使用情况(用来区分成员变量与成员函数参数,把对象当做返回值与其他对象进行交互)
  • class Intger
    {
    	int num;
    public:
        //构造函数
    	Intger(int num)
    	{
    		this->num = num;
    	}
        //普通成员函数(返回对象)
    	Intger& add(int num)
    	{
    		this->num += num;
    		return *this;
    	}
        //普通成员函数
    	void sub(int num)
    	{
    		this->num -= num;
    	}
    };
  • 常对象与常成员函数:当对象在定义时被const修饰后,就会成为常对象,同理成员函数在定义时被const修饰后,就也会成为常成员函数(注意格式)。    常对象只能调用常成员函数,但非常对象可以既可以调用常成员函数,也可以调用普通成员函数,而且常成员函数和普通成员函数同名,可以构成重载。
  • class Intger
    {
    	int num;
    public:
        //构造函数
    	Intger(int num)
    	{
    		this->num = num;
    	}
        //非常成员函数
    	void show(void)
    	{
    		cout << "no const" << num << endl;
    	}
        //常成员函数
    	void show(void) const
    	{
    		cout << "const" << num << endl;
    	}
    };
  • 析构函数:在类的对象被销毁时,自动调用,对对象的内容进行最后处理。
  • class Student
    {
    	char* name;
    	char sex;
    	short age;
    public:
    	//构造函数
    	Student(char* _name,char _sex,short _age)
    	{
    		cout << "----start----" << endl;
    		name = new char[strlen(_name)+1];
    		strcpy(name,_name);
    		sex = _sex;
    		age = _age;
    		cout << "====start====" << endl;
    	}
    	//拷贝构造函数
    	Student(Student & that)
    	{
    		cout << "----kaobei----" << endl; 
    		name = new char[strlen(that.name)+1];
    		strcpy(name,that.name);
    		sex = that.sex;
    		age = that.age;
    		cout << "====kaobei====" << endl;
    	}
    	//析构函数
    	~Student(void)
    	{
    		cout << "----end----" << endl;
    		int fd = open("stu.txt",O_APPEND|O_CREAT|O_RDWR,0777);
    		char buf[127] = {};
    		sprintf(buf,"name:%s sex:%c age:%hd\n",name,sex,age);
    		write(fd,buf,strlen(buf));
    		delete [] name;
    		cout << "====end====" << endl;
    	}
    	//普通成员函数
    	void show(void)
    	{
    		cout << "show_no_const" << endl;
    	}
    	//成员常函数
    	void show(void) const
    	{
    		cout << "show_const" << endl;
    	}
    };
  • 赋值构造函数:一个已经定义的对象给另一已经定义的对象进行赋值。   自定义赋值构造函数(确定格式、防止自赋值、释放旧资源、申请新资源、拷贝新内容)
  • class Student
    {
    	char* name;
    	//const char sex;
    	char sex;
    	mutable short age;//常对象调用常函数,强行修改该变量
    public:
    	Student(const char* _name = "",char sex = 'f',short age = 11)
    	:sex(sex),age(age)
    	{
    		name = new char[strlen(_name)+1];
    		strcpy(name,_name);
    	}
    	Student(const Student & that):sex(that.sex)
    	{
    		name = new char[strlen(that.name)+1];
    		strcpy(name,that.name);
    		age = that.age;
    	}
    	void show(void)
    	{
    		cout << name << " " << sex << " " << age << endl;
    	}
    	void show(void) const
    	{
    		cout << name << " " << sex << " " << age << endl;
    	}
    	void addAge(void) const
    	{
    		age++;
    	}
    	void operator = (Student & that)
    	{
    		cout << "----operator----" << endl;
    		if(this == &that) return;
    		//释放旧资源
    		delete [] name;
    		//申请新资源
    		name = new char[strlen(that.name)+1];
    		//拷贝新内容
    		strcpy(name,that.name);
    		sex = that.sex;
    		age = that.age;
    	}
    };
  • 静态成员与静态成员函数:所有类的对象都共享静态成员和静态成员函数,所以静态成员只在类内声明,在类外定义,静态成员函数并不传入this指针(但可以通过传参的方式传入对象指针),在《C++的基本概念二》的课后练习Timer内可以很好的说明。
  • 单例模式:一个类只能被创建出一个对象。以下有两个示例:
    class Singleton
    {
    	static Singleton intance;
    	Singleton()
    	{
    
    	}
    	Singleton(Singleton& that)
    	{
    
    	}
    	void operator = (Singleton& that)
    	{
    
    	}
    public:
    	static Singleton& getIntance(void)
    	{
    		return intance;
    	}
    };
    
    Singleton Singleton::intance;

    第一种:编译后就已经定义并初始化,对象只能获得该对象的地址,而无法再次创建该对象

    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;

    第二种:在第一次创建该类的对象时,可以创建成功,而后就无法再次创建。

    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值