栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++学习笔记(持续记录的随笔)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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"<     }
    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
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 e1 = std::make_unique();
        std::unique_ptr 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 
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开始)

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/997165.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号