C++基础-类和对象

第一个c++程序

cpp 复制代码
#include<iostream>
using namespace std;

int main()
{
	cout << "hello c++" << endl;
	return 0;
}

c++是兼容了大多数的c语言代码的,比如printf,但是c++也有自己的输入和输出。这里着重关注的是using namespace std;这个叫做命名空间,可以避免命名冲突或名字污染。上述代码不使用命名空间的代码如下

cpp 复制代码
#include<iostream>

int main()
{
	std::cout << "hello c++" << std::endl;
	return 0;
}

命名空间

cpp 复制代码
namespace x
{
	int a;
	int b;
}
int main()
{
	x::a = 10;
	x::b = 20;
	std::cout << x::a+x::b << std::endl;
	return 0;
}

这里有几个注意点

1.命名空间可以嵌套定义,命名空间名相同会进行合并。

2.命名空间中可以为变量,函数,类型等。

3.namespace只能定义在全局。

4.c++所有标准库都放在了std当中

c++的输入&输出

<iostream>是要包含的头文件,cin为标准输入,cout为标准输出,endl为换行加刷新缓冲区,<<为流插入,>>为流提取。这里需要注意我们在学习的时候使用using namespace std但在实际开发一般不会使用。

缺省函数

缺省函数是指在函数的定义中,给参数一个缺省值,当传参没有传递这个参数时,默认使用这个参数,全缺省就是每一个参数都有缺省值,半缺省就是部分参数缺省。要注意,缺省的参数必须连续,且从右往左。因为在实际调用中,会从左到右逐一赋值,不能跳跃。且当函数定义和声明分离时,不能同时给缺省值,规定在声明中给出缺省值。

cpp 复制代码
#include<iostream>
using namespace std;
//全缺省
void func1(int a=10,int b=20,int c=30)
{
	cout << a << "  " << b << "  " << c << endl;
}
//半缺省
void func2(int a, int b = 20, int c = 30)
{
	cout << a << "  " << b << "  " << c << endl;
}
//声明定义分离
void func3(int a = 10, int b = 20, int c = 30);
void func3(int a, int b, int c )   //此时不能同时给缺省参数
{
	cout << a << "  " << b << "  " << c << endl;
}
int main()
{
	func1();  //不传参使用默认参数
	func1(50);  //传参后使用传入参数
	func2(40);  //这时第一个参数必须给值
	return 0;
}

函数重载

c++支持在同一作用域下有同名函数,但要求参数不同,可以是个数不同,也可以是类型不同,这就叫做函数重载。

cpp 复制代码
#include<iostream>
using namespace std;

void func(int a,int b,int c)
{
	cout << a << "  " << b << "  " << c << endl;
}
void func(int a, int b) //个数不同
{
	cout << a << "  " << b  << endl;
}
void func(float a, int b, int c) //类型不同
{
	cout << a << "  " << b << "  " << c << endl;
}

int main()
{
	func(10, 20, 30);
	func( 20, 30);
	func(10.5f, 20, 30);
	return 0;
}

引用

类型& 引用别名 = 引用对象;

引用不是定义了一个新的对象,它和原本对象占用同一块空间,不单独开辟空间

cpp 复制代码
#include<iostream>
using namespace std;
int main()
{
	int a = 10;
	int& b = a;
	cout << a << endl; //输出10
	b++;
	cout << a << endl; //输出11
	return 0;
}

引用在定义时必须进行初始化,一个变量可以有多个引用,引用一旦引用一个实体后不可修改。

const引用

可以引用⼀个const对象,但是必须用const引用。const引用也可以引用普通对象,因为对象的访 问权限在引用过程中可以缩小,但是不能放大。

nullptr

cpp 复制代码
#ifndef NULL
    #ifdef __cplusplus
        #define NULL 0
    #else
        #define NULL ((void *)0)
    #endif
#endif

类和对象

cpp 复制代码
#include<iostream>
using namespace std;
class test
{
	int m_a;
	int m_b;
public:
	void myPrint()
	{
		cout << "myPrint" << endl;
	}
};

int main()
{
	test t;
	t.myPrint();
	return 0;
}

class为定义类的关键字,test为类名

一般默认为private,这个修饰的变量或函数,只能在类内进行访问,public定义类外也能访问,protected定义,类外不能访问但可以继承。

代码中test t是对这个类进行实例化。这里注意这个实例出来的对象大小是不算函数的,只算变量的大小。

this指针

前面说函数不算大小,那么是怎么确定是哪个对象调用的呢,就是通过this指针,这个指针是自带的

cpp 复制代码
void Init(Date* const this, int year,int month, int day)

类的默认成员函数

构造函数

构造函数函数名与类名相同,无返回值,默认构造函数参数为空,自己实现构造函数后,不会提供默认构造函数,实例化时会调用构造函数。无参构造函数,全缺省构造函数,不写构造函数默认生成的构造函数都叫默认构造函数,三者只能存在一个。

cpp 复制代码
class test
{
	int m_a;
	int m_b;
public:
	void myPrint()
	{
		cout << "myPrint" << endl;
	}
	//无参构造函数
	test() 
	{
		m_a = 1;
		m_b = 2;
	}
	//有参构造函数
	test(int a,int b)
	{
		m_a = a;
		m_b = b;
	}
};

析构函数

析构函数与构造函数相反,实在对象销毁的时候进行调用。析构函数名实在类名前加上~,一个类只能有一个析构函数。一般来讲,类中申请了空间要靠析构函数来释放。

cpp 复制代码
	//析构函数
	~test()
	{
		cout << "析构函数" << endl;
	}

拷贝构造函数

首先拷贝构造是一个特殊的构造函数。构造函数第一个参数为自身类型的引用,其余参数都有默认值,则这个构造函数叫做拷贝构造函数。这里第一个参数必须为引用,如果使用传值方式会有如下问题

这里调用拷贝构造进行创建d2,但是使用值传递,需要创建一个d1的临时变量,创建临时变量使用的是拷贝构造,而我们写的拷贝构造又是值传递,因此会陷入无穷

若未自定义拷贝构造,编译器会自己生成一个浅拷贝的拷贝构造函数,一般定义拷贝构造函数会加一个const修饰,防止改变原对象的值。且注意默认拷贝构造函数是浅拷贝,对与存在申请空间的类要使用拷贝构造函数申请空间,实现深拷贝。

拷贝构造的调用时机在如下三个部分

cpp 复制代码
#include<iostream>
using namespace std;
class test
{
public:
	int m_a;
	int m_b;
	test(int a, int b)
	{
		m_a = a;
		m_b = b;
	}
	void myPrint()
	{
		cout << m_a << m_b << endl;
	}
	test(const test& t)
	{
		m_a = t.m_a;
		m_b = t.m_b;
	}
};
int main()
{
	test t1(1,2);
	t1.myPrint();
	//使用已创建对象初始化新对象
	test t2(t1);
	t2.myPrint();
	//值传递的方式给函数参数传值
	//这里就是我前面说的为什么必须使用引用传递,不使用值传递,值传递会调用拷贝构造函数
	//值方式返回局部对象
	//return t1;
	return 0;
}

赋值运算符重载

当运算符用于类类型对象时,c++允许我们通过运算符重载对运算符赋予新的含义。但是重载运算符必须有一个类类型参数,不能重载改变内置类型对象的含义。且c++规定,类类型使用运算符时,必须调用运算符重载,否则会报错。若运算符重载函数未成员函数,则第一个变量默认为this指针,进行了隐式传递。重载<<>>必须使用全局函数,因为作为成员函数,第一个参数默认为this指针,此时参数出现了顺序问题。在重载++的时候函数名都是一样的,为了进行区分,后置++加了一个int形参。示例如下

cpp 复制代码
#include<iostream>
using namespace std;
class test
{
public:
	int m_a;
	int m_b;
	test(int a, int b)
	{
		m_a = a;
		m_b = b;
	}
	void myPrint()
	{
		cout << m_a << m_b << endl;
	}
	//重载前置++
	test& operator++()
	{
		test temp(m_a, m_b);
		this->m_b++;
		return temp;
	}
	// 重载后置++
	test& operator++(int)
	{
		m_b++;
		return *this;
	}
	//重载运算符时成员函数
	void operator+(test& b)
	{
		m_a += b.m_a;
	}
};
//重载运算符为全局函数
bool operator==(test& a, test& b)
{
	if (a.m_a == b.m_a && a.m_b == b.m_b) 
	{
		return true;
	}
	return false;
}
//重载<<>>必须使用全局函数
ostream& operator<<(ostream& out,test& t)
{
	out << t.m_a << t.m_b << endl;
	return out;
}
int main()
{
	test t1(1,2);
	test t2(1,2);
	if (t1 == t2) 
	{
		cout << "相等" << endl;
	}
	t1 + t2;
	cout << t1.m_a << endl;
	cout << t1;
	cout << t1++;
	cout << t1;
	cout << ++t1;
	cout << t1;
	return 0;
}

赋值运算符重载用于完成已经存在的两个对象进行直接的拷贝赋值,与拷贝构造不同,拷贝构造是创建出新的对象。规定其必须重载为成员函数,参数建议写为const当前对象引用,返回值建议写为当前类型的引用,当没有显示的定义,默认会生成类似浅拷贝的样子。

cpp 复制代码
#include<iostream>
using namespace std;
class test
{
public:
	int m_a;
	int m_b;
	test(int a, int b)
	{
		m_a = a;
		m_b = b;
	}
	void myPrint()
	{
		cout << m_a << m_b << endl;
	}
	test& operator=(test& t)
	{
		m_a = t.m_a;
		m_b = t.m_b;
		return t;
	}
};
int main()
{
	test t1(1,2);
	test t2(2,4);
	t1 = t2;
	t1.myPrint();
	return 0;
}

取地址运算符重载

const修饰的成员函数称之为const成员函数,修饰放在成员函数列表后面,其修饰后,隐含this指针由Date* const this 变为 const Date* const this

cpp 复制代码
#include<iostream>
using namespace std;
class test
{
public:
	int m_a;
	int m_b;
	test(int a, int b)
	{
		m_a = a;
		m_b = b;
	}
	void myPrint() const
	{
		cout << m_a << m_b << endl;
	}

};
int main()
{
	test t1(1,2);
	//权限缩小
	t1.myPrint();
	return 0;
}

初始化列表

构造函数还有另一种初始化方式,就是初始化列表。每个成员变量在初始化列表中只能出现一次。引用成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进行初始化,否则会编译报错。因为C++规定,对象的"初始化"发生在进入构造函数函数体(即花括号 {})之前,而不是在函数体内部。这些变量不能先定义后赋值,必须定义和赋值一起完成。

无论是否显示写初始化列表,每个构造函数都有初始化列表;

无论是否在初始化列表显示初始化成员变量,每个成员变量都要走初始化列表初始化;

cpp 复制代码
class MyClass {
private:
    const int m_const;
    int& m_ref;
    Other m_other;
public:
    // 初始化列表:在对象"出生"的一瞬间完成所有设定
    MyClass(int a, int b) 
        : m_const(a),    // 出生即赋值常量
          m_ref(a),      // 出生即绑定引用
          m_other(b)     // 出生即调用带参构造
    {
        // 函数体内什么都不用写了,所有成员都已经初始化完毕
    }
};

类型转换

cpp 复制代码
#include<iostream>
using namespace std;
class A
{
public:
    // 构造函数explicit就不再支持隐式类型转换 
    // explicit A(int a1)
    A(int a1)
        :_a1(a1)
    {}

    //explicit A(int a1, int a2)
    A(int a1, int a2)
        :_a1(a1)
        , _a2(a2)
    {}

    void Print()
    {
        cout << _a1 << " " << _a2 << endl;
    }

    int Get() const
    {
        return _a1 + _a2;
    }
private:
    int _a1 = 1;
    int _a2 = 2;
};

class B
{
public:
    B(const A& a)
        :_b(a.Get())
    {}

private:
    int _b = 0;
};

int main()
{
    // 1构造一个A的临时对象,再用这个临时对象拷贝构造aa3 
    // 编译器遇到连续构造+拷贝构造->优化为直接构造 
    A aa1 = 1;
    aa1.Print();


    // C++11之后才支持多参数转化 
    A aa3 = { 2,2 };

    // aa3隐式类型转换为b对象 
    // 原理跟上面类似 
    B b = aa3;
    return 0;
}

这里直接将1赋值给了类对象,就会进行类型转换。

static成员

static修饰称为静态成员变量,要在类外进行初始化,修饰函数,称为静态成员函数,没有this指针。

cpp 复制代码
#include<iostream>
using namespace std;
class test
{
public:
	static int m_c;
	void static myStatic()
	{
		cout << "static" << endl;
	}
};
int test::m_c = 10;
int main()
{
	test::myStatic();
	cout << test::m_c << endl;
	return 0;
}

友元

友元提供了一种突破访问限定符的访问方式。友元分为友元函数和友元类,将某一类或函数注册为自己的友元,该类或函数就可以访问自己的private限定的变量或函数。

cpp 复制代码
#include<iostream>
using namespace std;
class test
{
	//友元函数
	friend void func(const test& t);
	//友元类
	friend class test1;
private:
	int _a = 10;
	int _b = 20;
};
class test1
{
	int _c;
	int _d;
public:
	test1(const test& t)
	{
		_c = t._a;
		_d = t._b;
	}
	void myPrint()
	{
		cout << _c << _d << endl;
	}
};
void func(const test& t)
{
	cout << t._a << t._b << endl;
}
int main()
{
	test t;
	func(t);
	test1 t1(t);
	t1.myPrint();
	return 0;
}

内部类

一个类内部在声明一个类,这个类就叫内部类。内部类默认为外部类的友元类。注意定义外部类不包含内部类

cpp 复制代码
#include<iostream>
using namespace std;
class test
{
public:
	class test1
	{
		int a;
		int b;
	public:
		test1(const test& t)
		{
			a = t._a;
			b = t._b;
		}

	};
private:
	int _a = 10;
	int _b = 20;
};
int main()
{
	test t;
	test::test1 t1(t);
	return 0;
}

匿名对象

这个其实就是不用变量进行接收,会调用一次构造函数和一次析构函数就会结束声明周期

相关推荐
ysa0510301 小时前
【板子】费用流
算法·深度优先·图论
玖玥拾1 小时前
LeetCode 80 删除有序数组中的重复项 II
算法·leetcode
青山木2 小时前
Hot 100 --- 电话号码的字母组合
java·数据结构·算法·leetcode·逻辑回归
闪电悠米2 小时前
力扣hot100-240.搜索二维矩阵2-单调性剪枝详解
数据结构·算法·leetcode·矩阵·哈希算法
imuliuliang2 小时前
关于基于图论的最短路径算法性能对比分析7
算法
SilentSlot2 小时前
【C/C++】手写 DPDK 协议栈(十二):TCP 并发与自实现 epoll 的就绪事件分发
c语言·c++·tcp/ip
SilentSlot2 小时前
【C/C++】手写 DPDK 协议栈(六):TCP 三次握手、状态机与数据回显
c语言·c++·tcp/ip
乱七八糟的屋子2 小时前
AMQP C++ 超详细实战教程(amqp-cpp 开源库从零入门到生产落地)
c++·rabbitmq·任务队列·amqp·流量削峰·c++服务端异步解耦·分布式消息通信
hurrycry_小亦2 小时前
洛谷题目:P1233 [ICPC 2001 Taejon R] 木棍加工 题解(本题较难)
c++·算法·贪心算法·动态规划