IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++学习笔记(持续记录的随笔) -> 正文阅读

[C++知识库]C++学习笔记(持续记录的随笔)

C++和C的区别:

1:使用了命名空间

2:作用域标识符? ::

3:结构体可当类使用,定义方法等。

4:引用类型 &,在C中是取地址

5:函数重载(操作符重载)

6:面向对象

注意点:

结构体和类的区别在于,结构体中的变量和方法都是public,而类默认是private

const的用法:

<1

const在C或者是C++中被指的是不可改变的,在类中可以看做是一种可见性 ,它更像是一种承诺,承诺该值不被改变,但也是可以绕过其承诺比如通过逆向引用其地址可以修改const修饰的值

? ? std::cout << "Hello World!\n";

? ? const int a = 12;
? ? int* b = NULL;
? ?
? ? b = (int*) & a;
? ? *b = 14;
? ? std::cout << *b << std::endl;
? ? std::cout << a << std::endl;

为了更加严谨的保证承诺应修改为const??int * b ,?const放*前面,保证b这个指针可以修改其地址指向别人,但他指向的内容不能被修改,被网友称为常量指针,它还有另一种写法 int const * b(只要保持在const在*前则为常量指针)。于此相反,const放在*后面int * const b则内容可以更改,但指针的值也就是b也就是所指向的地址不能被修改,这就是引用&的本质。在网上被称为指针常量。而const int * const b,则指向的内容和指向的地址都不能被改变。

<2

在类中被const标记的方法则不允许类成员的值发生改变 如int getter() const;

在该类方法中则不允许类成员的值发生改变。而以mutable(意味着这个值是可以被改变的)修饰的变量则可以在const标记的方法中改变其值。

<3 带const标记和不带const的同名方法可被重载(返回值类型和参数类型都相同),默认情况下则会调用const标记的方法。

构造函数的初始化列表

初始化值的顺序与类成员变量声明的顺序保持一致,因为创建对象时会先调用成员初始化列表,不在列表中的值也会被初始化,所以用了初始化列表后为了避免性能上的浪费(多次初始化),在初始化列表中初始化所有成员变量。比如

class example {
public:
    int s;
    example()    
    {
        std::cout << "i am inited";
    }
    example(int x) {
        std::cout << x << std::endl;
    }
   
};
class test {
public :
    int a;
    example c;
    test()
        :a(1)  
    {
        c = example(7);
    }

};

定义类test和初始化列表时则会调用example的默认初始化构造函数,在构造函数里又重新定义则在栈上创建了两个example对象。已经用const修饰的成员变量只能用初始化列表初始化且必须被初始化。

而当类成员是一个类的实例,且该实例中有变量用const修饰,则该类成员必须用初始化列表实现,而不能在其构造函数中,编译器也会报错,因为const修饰的值只能在初始化列表中被初始化且不能修改,用了初始化列表则按类成员声明都被初始化一次,在构造函数中再初始化,该类成员又会调用其构造函数又初始化一遍const,显然这是不被允许的。


class example {
public:
? ? ?const int s;
? ? example()
? ? ? ? :s(78)
? ? {
? ? ? ? std::cout << "i am inited"<<std::endl;
? ? }
? ? example(int x)
? ? ? ? :s(1)
? ? {
? ? ? ? std::cout << x << std::endl;
? ? }
? ?
};
class test {
public :
? ? int a;
? ? example c;
? ? test()
? ? ? ? :a(1)
? ? {
? ? ? ? c = example(6);
? ? }

};

比如标红的这句代码不被允许

<4 const指针只能调用const方法

new和malloc的区别

都是分配内存的大小,而new仅仅只是调用了其构造函数(如创建类对象)都是在堆上创建空间,注意释放内存),在一个特定的地址初始化对象。

Entity*p=new(指向内存的指针)Entity();

?例如

int* b = new int(50);
?? ?Entity* p = new(b)Entity();
?? ?std::cout << b << std::endl;
?? ?std::cout << p << std::endl;

隐式构造函数

当一个类中有多个重载的构造方法接受一个参数时,若是直接以值直接赋值给对象的形式则为隐式构造函数。在函数传参进行隐式构造对象时,字符串赋值可以用std::string(“abcc”)或直接

Entity(“avc”)

#include <iostream>
using namespace std;
class Entity {
public:
?? ?int num;
?? ?std::string name;
?? ?Entity() :num(1), name("sda")
?? ?{

?? ?}
?? ?Entity(const std::string& name) :num(-1),name(name)
?? ?{

?? ?}
?? ??
?? ??
};
void Printmessage(const Entity &t) {
?? ?std::cout << t.name << t.num << std::endl;
}
int main()
{
?? ??
?? ?Entity t1 = "sda";
?? ??
?? ?Printmessage(t1);
?? ?Printmessage(std::string("dada"));
?? ? ?? ?
?? ?return 0;
}

这种写法通常用于简化代码,实际用到少。

而用explicit修饰构造函数则不允许调用隐式类型转换,也就是只允许显式调用其构造函数。如

explicit???Entity(const std::string & name?:num(1), name(name)
?? ?{

?? ?}

?除非使用强制类型转换

Entity e=(Entity)"sdad";

操作符的重载

本质上就是函数。

格式:

返回值类型 operator? 操作符号 (参数){

函数体

}

比如

class Entity {
public:
	int num;
	std::string name;
	Entity() :num(1), name("sda")
	{

	}
	explicit Entity(const std::string& name) :num(-1),name(name)
	{

	}
	 
	 
};
std::ostream& operator<<(std::ostream&   stream, const Entity& e) {

	stream << e.name << "," << e.num;

	return stream;
 }
int main()
{
	 
	Entity t1 = (Entity)"sda";
	 
	std::cout << t1 << std::endl;
	 
	 	
	return 0;
}

类中的this关键字

在类中this关键字是指当前类对象的指针,且该指针的值也就是其指向的地址不允许发生修改,它是一个指针常量。

作用域

像空作用域 {} 其他的如 if for while 函数体内,类内都是作用域。在作用域内使用堆开辟内存则需要手动释放,即使是用了智能指针也是帮我们在离开作用域时做了手动释放内存。而一般的定义变量则是在栈开辟的内存,离开作用域后自动释放。

?? ?{
?? ??? ?Entity* t1 = new Entity();?? ??
?? ?}

?t1在离开{}作用域后被释放,但其指向的空间由于是堆开辟的则未被释放。

智能指针

?std::unique_ptr 作用域指针

? ?{

????????std::unique_ptr<Entity> e1 = std::make_unique<Entity>();
? ? ? ??std::unique_ptr<Entity> e2(new Entity());

}

?为什么叫unique_ptr?它表示你不能复制它给第二个unique_ptr,它的拷贝构造函数是已经被删除的了。如果存在两个unique_ptr指向同一个内存地址,当一个unique_ptr超出作用域被释放时,它的一个复制unique_ptr将指向无效的区域,显然这是不被允许的。

unique_ptr在离开作用域后自动释放堆分配的空间。

unique_ptr的构造函数是用explicit修饰的,则意味着只能通过显式构造创建。它是一个栈分配的对象,但栈对象死亡时,它会调用delete在其指针上以释放内存空间,它是低开销甚至没有开销的。

std::make_unique是c++ 14的引用的,C++11并不支持,好处在于可以抛出创建对象时构造函数异常,而不会得到一个没有引用的悬空指针造成内存泄漏,可以认为是异常安全的。

std::share_ptr? 共享指针

它则允许被复制,它被创造时需要分配另一块内存用于引用计数,复制该共享指针一次则引用计数+1,释放一次引用计数-1,只要其引用计数不为0,共享指针所指向的内存区域都不会被释放。

目前我认为本质是是一种以控制栈对象的释放来控制堆区域内存的释放。

std::weak_ptr 弱指针

但把一个共享指针复制给一个弱指针时,引用次数不会+1,但其共享指针被释放后,weak_ptr则指向无效区域。

智能指针为自动化内存管理提供了便利。

拷贝构造函数(默认浅拷贝需要自己写深拷贝)

如果一个类中有指针这类数据类型,用一个已有的该类对象去初始化,如果不自己实现深层拷贝构造函数,那么类本身是有一个浅层的拷贝构造函数(复制原对象给新对象),所以指针这类数据的值也被复制了,所以调用默认的浅层拷贝构造创建的对象中的指针指向的是同一个内存空间,而其他的数据类型(包括指针本身所占内存)都被重新分配了空间。比如用A去初始化B,问题在于A出了作用域调用析构函数时,A的指针所指向的内存空间被释放,该被释放的空间已经被标记为不属于该对象,B调用析构函数二次释放出现问题。所以自己需要定义一个深层拷贝构造函数:再次为指针开辟新的空间,并拷贝其值。

比如

#include <iostream>
using namespace std;
class Entity {
public:
	int a;
	int* b;
	Entity(int v_a) :
		a(v_a)
	{
		std::cout << "haved been created" << std::endl;
		b = new int;
		*b = 12;
	}
	Entity(const Entity& t)
		:a(t.a)
	{
		this->b = new int;
		*(this->b) = *(t.b);


	}
 
	~Entity() {

		std::cout << "haved been destroyed" << std::endl;
		delete this->b;
	}
	 
};
 
int main()
{
 
	{
		Entity t1(2);
		Entity t2(t1);
		*(t1.b) = 99;
		std::cout << *(t1.b) << std::endl;
		std::cout << *(t2.b) << std::endl;

		std::cout << t1.b << "," << t2.b << std::endl;
	}
		return 0;
}

在函数传递对象且不改变对象内容时,总是使用const +对象的引用形式去传递(考虑到深层拷贝消耗内存导致程序变慢)

动态数组

std::vector<元素类型> 动态数组名

获取大小? .size()

.push_back()新增元素

.clear() 清除所有元素

.erase()清除某个元素 参数为迭代器 .begin()+1 (移除第2个元素从0开始)

.reserve(x)

当动态数组做函数参数时,使用引用类型(避免在栈内开辟大量空间)

对于动态数组效率的优化:

<1>与编译器的环境有关,一般创建动态数组时,默认数组大小为1,这意味着每次添加新的元素都会将数组进行扩容然后再拷贝,效率慢。一个有效的方法是通过.reserve(x)告诉动态数组里面初始会存多少个元素,虽然这只是一个大概值,能减少从1到x容量的拷贝。(其中x是动态数组中元素容量的大小)

<2>使用emplace_back代替push_back(),在这种情况下不是传递已新建对象,而是传递构造函数的参数列表,并让动态数组使用该参数列表新建对象。

静态链接和动态链接库

区别:

静态链接库会放入可执行文件中参与整个编译过程,而动态链接库是运行时链接的,你可以选择在程序运行时,装载动态链接库,以节省内存资源。静态链接可以产生更快的应用程序。而当动态链接库被运行时的程序装载时,程序的部分将被补充完整。

DLL是一种运行时的动态链接库lib文件也被称为是静态连接库。

对于静态链接库:

xxx.lib为静态库,设置环境,如库里面的函数声明头文件的目录,包含库目录以及附加依赖项(库名称)

对于动态链接库:

xxxdll.lib为动态库,里面包含一堆函数指针指向dll 可执行文件

?

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-03 10:32:27  更:2022-07-03 10:34:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/27 22:35:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码