C++学习笔记day2

C++学习笔记day2,第1张


前言

本文主要总结了C++中基础的构造函数、拷贝构造函数、析构函数、关键字const、static以及重载分别进行了讨论,对不同函数的特点、作用、调用时机及注意事项,例如:C++默认存在的5种函数、5种不可重载的运算符,设计模式之一的单例模式进行了总结归纳,强化基础知识掌握能力!!


提示:以下是本篇文章正文内容,下面案例可供参考

一、构造函数 1.1 规则、特点(有参、无参)
定义:构造函数是一种创建对象时自动调用的函数,是c++中特殊成员函数
作用:创建对象时初始化对象,为对象的成员变量赋值,仅初始化一次且可根据条件进行初始化

规则和特点:
1.名字必须与类同名 无返回值 可能有参数
2.访问权限一般是public,一旦私有构造函数,那么在类的外部将不能创建对象。
3.不能手动调用 只有在类对象创建时 自动调用		
4.实例化(创建)对象时,只用到一个构造函数

无参构造函数:
#include 
using namespace std;

class Person
{
	string name;
public:
	//构造函数 格式:1.函数名和类名相同 2.没有返回值 3.参数可有可无
	Person() //无参构造函数
	{
		cout<<"构造函数"<
1.2 默认构造、类外实现
默认构造函数即: 显示(手动编写构造函数)
1、默认构造函数,当没有显式的定义构造函数时,系统会自动生成一个默认构造函数。
2、默认的构造函数:没有参数也没有逻辑;仅仅为了有构造函数而存在。
3、一旦显式的定义了构造函数,将不会生成默认构造函数

例如:
#include 
using namespace std;
class Person
{
private:
	int age;
public:
(1)如果这里不显示的定义构造函数,会有一个默认构造函数,没有参数没有逻辑
	所以创建对象的时候不能有参数

(2)因为在这里显示的定义了一个构造函数,所以现在整个类中只有一个构造函数,
所以创建对象的时候必须给实参列表
	Person(int a)  
	{
		age = a;
	}
};
int main()
{
	Person p(1);
	return 0;
}

1.默认的构造函数: 类中没有显示的定义构造函数
2.默认构造函数格式:没有参数 没有逻辑 只是为了存在而存在	
3.只要我们定义了构造函数,默认的构造函数就不存在了

构造函数的类外实现:即声明在类的内部,但函数实现在类外先,格式:类名::构造函数名
例如:
#include 
using namespace std;

class Person
{
	int age;
public:
	Person();
	Person(int a);
	
};

Person::Person()
{
	cout<<"create..."<
1.3 初始化列表、this指针
构造函数的初始化列表:冒号形式引出初始化列表  格式:成员变量名(初始值)
注意事项:
1.初始化成员列表的顺序应该和成员声明的顺序保持一致,不建议使用成员初始化其他成员;
2.成员的初始化顺序,是按照声明的顺序进行,和初始化列表的顺序没有关系。
例如:
class Rect
{
private:
	int width;
	int height;
public:
	Rect(int w, int h):width(w),height(h){}
                       //1.初始化列表,用w初始化成员width,用h初始化成员height,
                       //2.初始化列表只能初始化成员变量  ()可以是构造函数的参数,也可是常量
	int perimeter()
	{
		return width*2+height*2;
	}
};

初始化列表分文件实现:实际编写代码中,代码量较大,为了方便阅读、修改、调试,会将函数的声明、实现、调用分文件写,即头文件中放需要添加的头文件、类的声明、函数的声明;函数的具体功能实现写在.cpp中 函数调用在main 中调用

this指针作用:类的每个成员函数中都有一个指针叫this,指向调用成员函数的那个对象。
1.是一个指针,指向当前正在调用成员函数的对象
2.this只能在类的内部使用,在类的内部调用成员都是使用this调用的,可以省略
3.存在的意义是在类的内部,将当前对象当做参数进行传递
例如:
class Person
{
private:
	int age;
public:
	Person(int age)
	{
		this->age = age;//this->age 是指向当前构造函数的对象的age
	}
	void show()
	{
		cout<age<
二、拷贝构造函数
声明:函数名(类名)(const 类名 &对象名)  	Person(const Person& other)
拷贝构造函数的参数列表必须是 const 类名 &对象名
2.1 默认拷贝构造
默认拷贝构造函数:
1、在没有显式的定义拷贝构造函数时,系统会自动生成一个拷贝构造函数,功能:将成员变量逐个赋值 
2、如果显式的定义拷贝构造函数,并且运行时执行了拷贝构造函数,那么默认拷贝构造函数和默认构造函数都不会被调用了
3、创建一个对象的时候,只会调用一个构造函数(默认拷贝构造也是构造函数)

注意:
默认的构造:没有显示定义构造函数都没有
默认的拷贝构造:没有拷贝构造存在

写了拷贝构造的时候,默认的构造函数还在不?  不再了,因为拷贝构造也是构造函数,只要类中有构造函数,那么默认的构造函数就不在了。

总结:
1. 如果没有自定义的拷贝构造函数则系统自动生成一个默认的拷贝构造函数

Empty(const Empty& o)
{
this->age = o.age;
}

2. 当采用直接初始化或复制初始化实例化对象时,系统自动调用拷贝构造函数
Person p;
Person p2 = p;
Perosn p3(p);

3. 系统默认拷贝构造函数的功能:逐个对成员变量进行赋值

4. 显示的定义了拷贝构造函数,系统默认的就不存在了,
   函数的功能(对变量进行的赋值),就由我们自己来完成了
2.2 3种调用情况、私有化(构造函数、拷贝构造函数)
拷贝构造函数调用三种情况:
第一种:程序中需要创建一个新对象 并用另一个同类的对象对它初始化 
int main()
{
	Person* p = new Person(12);//构造函数
	Person * p1 = new Person(*p);//调用拷贝构造
	p1->show();//12
	
	Person a(2);//构造函数
	Person b = a;//调用拷贝构造
	b.show();//2
	Person c(a);//调用拷贝构造
}

第二种:当函数的参数为类的对象时
*******示例1*******************
void farsight(Person p) //Person p= a;
{
}
int main()
{
	Person a(19);
	//使用对象a初始化函数的参数p,a和p是两个独立的对象
	farsight(a);//会调用拷贝构造
}
第三种:函数的返回值为类对象
如果对象生命周期大于函数,会创建一个新的对象,促发拷贝;
如果函数结束对象就被删除,不会创建新的临时对象,不会促发拷贝

//1.返回的是栈空间对象:没有拷贝,等价于编译器做了优化,栈-->栈 使用同一个 换了一个名字
//2.返回的是堆空间对象:调用拷贝, 把堆空间的对象,复制一份到 栈空间
Person fun2()
{
   // Person p;//栈空间 无参构造  //1
   // cout<<&p<
2.3 深拷贝、浅拷贝
浅拷贝:拷贝指向对象的地址,本质是地址的复制,两个指针指向同一个对象
深拷贝:拷贝指向的对象,本质是对象的复制,两个指针指向不同的对象

注意事项:
浅拷贝容易出现的问题:如果前面的指针将指向的对象释放掉了,那么后面的指针都不可使用且会报错
系统默认是浅拷贝

三、析构函数 3.1 特点、作用、调用时机
析构函数是一个特殊的成员函数 作用与构造函数相反
功能:对象所在的函数已经调用完毕,系统自动调用析构函数去释放空间,避免内存泄漏
声明:没有返回值 名字必须与类名同名 没有参数 名字前 ~
~Person()
{		
	cout<<"析构"<
3.2 默认析构函数
默认的析构函数:没有参数,没有逻辑 为存在而存在
~Person()
{
	
}

但我们可在默认的析构函数里添加功能,释放堆空间对象数组
四、类、对象 4.1 类对象初始化顺序
类成员的初始化顺序:先成员,后构造
初始化顺序:先按照成员变量的声明顺序初始化成员变量,最后调用构造函数

匿名对象
作用:1、赋值  2、初始化  3、传参数
语法:类型(构造函数的参数列表)
注意:匿名对象的生命周期非常的短(临时的值)  匿名对象不是一个必要的语法,它能让我们的代码变的更加简洁。

示例1:给形参赋值
int main()
{
//创建匿名对象作为函数的参数,省去了额外创建一个对象的步骤,代码简洁了!
    	fun(Person(18, 100));	
	//这是不使用匿名对象的情况
	Person p(18, 100);
	fun(p);
	return 0;
}
示例2::初始化数组
 Person ps[5] = {Person(1), Person(2), Person(3), Person(4), Person(5)};
 for(int i = 0;i < 5;i++)
 {
      ps[i].show();
 }
4.2 const(常函数、常对象)与mutable
常函数特点:
1.常函数中的this是被const修饰的,在常函数中成员变量不能被修改;
2.常函数只能调用常函数;
class Person
{
	int age;
public:
	Person(){}
	Person(int a):age(a){}

	//常函数:1.不可以改变成员变量的值   2.常函数只能调用常函数
	void show() const 
	{
		//++age;错误,值不可以修改
		cout<
4.3 友元(函数、对象)
友元概念:友元可以访问与其好友关系的类中的私有(private)成员,使用friend关键字进行修饰	  
注意: 但友元破坏类的封装特性,慎用!

友元的特点:1、友元是单向的 2、友元不能传递  3、友元不能继承
1)友元是单向的
class B;
class A
{
friend class B;
};
//B是A的友元,但是A不是B的友元。

2)友元不能传递   B是A的友元,C是B的友元,A和C没有友元关系。
3)友元不能继承   

友元分为:友元函数和友元类
1、友元函数(友元全局函数):将普通函数声明为友元函数  friend + 函数声明
#include 
using namespace std;

//访问好友关系类中的 私有的东西
class Person
{
 //全局函数display是我的朋友
 friend  void display(Person& p);
private:
    int age;
public:
    Person(int age):age(age){}
    void setAge(int age){this->age = age;}
    void show(){cout<
#include 
using namespace std;

class Worker
{
	friend class Student;
};
//类的声明
class Teacher;
class Student
{
friend class Teacher;
private:
	string name;
	int score;
	void sleep()
	{
		cout<<"slepp"<

 

 

五、单例模式
1.单例模式的作用与目的
作用:保证一个类仅有一个实例,并提供了一个访问它的全局访问点
目的:为了保证类的实例对象只有一个
主要解决:一个全局使用的类 频繁的创建于销毁
关键: 构造函数和拷贝构造私有化

2、实现方法
1.私有构造函数和拷贝构造函数   不让类的外部创建对象
2.静态类指针     在静态成员函数中实现只创建一个对象的逻辑
3.静态成员函数返回类指针  用来实例化(new)对象
5.1 static(静态成员函数及变量)
c++中静态成员:
1.属于整个类 静态成员变量只存储一份供所有对象使用,所有同类型的对象共享静态变量
2.必须在类外单独初始化 而且只能全局进行 否则不会分配空间 编译报错
#include 
using namespace std;

class A
{
public:
   static int num;
};
//1.初始化之后,才会对变量分配空间 2.类外进行单独的初始化
int A::num = 10;

int main()
{
    //3.使用静态变量,可以直接只用 类::的方式 获取变量
   cout<
using namespace std;

class A
{
public:
   static int num;
   int age;
   A(int n):age(n){}
   static int getNum()
   {
     //静态成员函数没有this指针,就不能使用类中普通的成员变量
     //静态函数只能访问静态的成员变量
     //cout<age< 

5.2 单例模式实现方法
*******************************one.h
#ifndef ONE_H
#define ONE_H

class One
{
    //1.私有构造和拷贝
    One();
    One(const One& other);
    //2.静态指针
    static One* instance;

    int num;
public:
    //3.静态函数:1.创建对象  2.返回静态指针
    //通过静态全局访问口  获取对象指针
    static One* getInstance();

    int getNum()
    {
        num++;
        return num;
    }
    void setNum(int n)
    {
        num = n;
    }

    ~One();
};

#endif // ONE_H

***************************cpp	
#include "one.h"
#include 

//cpp文件 进行初始化
One* One::instance = NULL;

One::One(){ num = 1;}
One::One(const One &other){}

One *One::getInstance()
{
    if(instance == NULL)
        instance = new One;
   return instance;
}

One::~One()
{
    if(instance != NULL)
        delete instance;
}

*******************************main
#include 
#include "one.h"
using namespace std;
/*
 * 设计模式之单例模式

1.单例模式的作用与目的
    作用:保证了一个类仅有一个实例,并提供了一个访问它的全局访问点
    目的:为了保证类的实例对象只有一个
    主要解决:一个全局使用的类 频繁的创建于销毁
    关键: 构造函数和拷贝构造私有化
2、实现方法
    1.私有构造函数和拷贝构造函数   不让类的外部创建对象
    2.静态类指针     在静态成员函数中实现只创建一个对象的逻辑
    3.静态成员函数返回类指针  用来实例化(new)对象
 * */
int main()
{
  One* o1 = One::getInstance();
  o1->setNum(10);
  One* o2 = One::getInstance();
  One* o3 = One::getInstance();

  cout<getNum()<getNum()<
六、重载(遮蔽)
运算符重载:运算符重载本质是函数重载,函数名以运算符的形式来命名,调用函数也是通过运算符来调用
作用:如果类没有重载运算符,类的对象不能进行运算符的 *** 作

1、定义
1.重载:给运算符重新赋予新的含义,在类的内部定义的运算符重载和成员函数是一样的
2.重载方法:定义一个重载运算符的函数 在需要执行被重载的运算符时,系统会自动调用该函数;
3.重载运算符格式:
	函数类型 operator 运算符名称(形参列表) 
	bool     operator     ==   (const Person &s);	
4.运算符重载的参数个数由运算符本身决定,但是类型可以自定义 
5.由运算符的左值调用运算符的重载
6.如果类没有重载运算符,类的对象不能进行运算符的 *** 作
注意:可以将函数的重载定义成全局的,但是不方便 <<  >>
void operator+(const pereson  other)
{
}
person p;
person p2;
p.operotor+(p2);成员函数一样  普通的调用
p+p2;自动调用

运算符重载虽然对返回值类型和参数类型没有要求,但是我们依然不能随便定义;返回值类型和参数的类型一定要符合习惯才能让代码变得更优雅。
6.1 运算符重载(+ >= ==  与=  & )
****************代码示例1:+号的重载**************
#include 
using namespace std;

class Person
{
    int age;
public:
    Person(int age):age(age){}
    void show(){    cout=运算符**************
#include 
using namespace std;
 
class Person
{
public:
	int _age;
	Person(int age):_age(age)
	{
	}
	int operator >=(Person& other)
	{
    		if(this->age>other.age)
         		return 1;
    		else
         		return 0;
 	}
};

int main()
{
	Person per1(14);
	Person per2(6);
	cout << (per1 >= per2) << endl;
	return 0;
}
**************示例3:==运算符重载**************
#include
using namespace std;

class Person
{
private:
	int age;
public:
	Person(int a)
	{
		age = a;
	}
	bool operator==(const Person &s);	//==运算符的重载 
};
//this是运算符的左值,参数是运算符的右值
bool Person::operator==(const Person &s)
{
	if(this->age==s.age)
	{
		return true;
	}
	else 
	{
		return false;
	}
}

int main()
{
	Person p1(20);
	Person p2(20);
	if(p1==p2)//这里之所以能够使用==运算符,是因为Person重载了==运算符
	{
		cout<<"the age is equal!"<
using namespace std;

class Person
{
    int age;
    string name;
public:
    Person(int a):age(a)
    {
    }
    Person(string name,int a):name(name),age(a)
    {
    }
   bool operator == (Person& other);
   bool operator == (int a);
   bool operator == (string n);
};
bool Person::operator ==(Person& other)
{
       if(this->age == other.age)
           return true;
       else
           return false;
}
bool Person::operator ==(string n)
{
    if(this->name == n)
        return true;
    else
        return false;
}
bool Person::operator ==(int a)
{
    if(this->age == a)
        return true;
    else
        return false;
}

int main()
{
    Person p(10);
    Person p1(20);

   if(p == p1)//p.operator==(p1)
   {
       cout<<"equal"<
using namespace std;

class Person
{
    int age;
    string name;
public:
    Person(int a):age(a){
        cout<<"构造"<age = other.age;
    }
   void show(){
       cout
6.2  默认存在函数(5) 与 不可重载运算符(5)
C++默认存在的函数
1)默认构造函数
2)默认拷贝构造函数
3)析构函数
4)默认=重载  
5)默认&重载   这是一个有争议的运算符重载。说它存在因为每个对象都能取地址;说它不存在没人会去重载取地址运算符。面试的时候就说它&是默认存在,知道就比不知道好。

不能重载的运算符 5个  
 ?:   sizeof()   . (成员运算符)  ::    .*  ->* (成员指针运算符)



示例7:&运算符
#include 
using namespace std;
 
class Person
{
public:
	int _age;
	Person(int age):_age(age)
	{
	}
	//以下的两个&重载函数,只能保留其一,因为不能构成函数的重载
	int* operator & ()
	{
		return &this->_age;
	}
	Person* operator&()
    	{
       	 	return this;
    	}
};
int main()
{
	Person per1(14);
	int *p = &per1;
	cout << p << endl;
	return 0;
}


++ -- 运算符重载
默认情况下为前缀形式 operator++(  )    前缀++p 
后缀形式需要添加一个int类型参数,参数无意义,
仅仅用于区分前缀和后缀形式  operator++(int a)  后缀p++
#include 
using namespace std;

class Person
{
private:
	int age;
public:
	Person(int age):age(age){}	
	Person& operator++()
	{
		cout<<"前缀形式"< 
总结 

这里对文章进行总结:

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1499021.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-25
下一篇 2022-06-25

发表评论

登录后才能评论

评论列表(0条)