13. C++类的简单理解

全面理解C++中的类

1. 类的访问属性:public,protect,private

C++中类的成员变量和函数都带有三种属性中的一种,假如没有特别声明,那么就默认是私有的(除了构造函数)。public表示是公开的,对象可以直接调用的变量或者函数;protect表示是保护性的,只有本类和子类函数能够访问(注意只是访问,本类对象和子类对象都不可以直接调用)而私有变量和函数是只有在本类中能够访问(有个例外就是友元函数,这个后面会详细说)

class A
{
public:A(int b):m_public(b),m_protected(1), m_private(2){}int m_public;
protected:int m_protected;
private:int m_private;
};
int main( void )
{A a(10);cout<<a.m_public<<endl; //正确,可以直接调用cout<<a.m_protected<<endl; //错误,不可以直接调用cout<<a.m_private<<endl;//错误,不可能直接调用
}

而子类对父类的继承类型也有这三种属性,分别为公开继承,保护继承和私有继承。

class A
{
public:A(int b):m_public(b),m_protected(1), m_private(2){}int m_public;
protected:int m_protected;
private:int m_private;
};
class B: public A{} //公有继承
class C: protected A{} //保护继承
class D:private A{} //私有继承

于是问题来了,父类成员的公开属性有三种,子类的继承属性也有同样的三种,那么一共就有九种搭配(例如公开继承父类公开成员,私有继承父类公开成员,保护继承父类私有成员等等)。

我们只需要记住这两个里取严格的那一种。例如私有继承父类公开成员,那么在子类里父类的所有属性都变成子类私有的了。

具体如下所示:

​ public: protected: private:
public继承 public protected 不可用
protected继承 protected protected 不可用
private继承 private private 不可用

class A
{
public:A(int b):m_public(b){}int m_public;
};
class B:private A
{
public:B(int num):A(num){}
};int main()
{B b(10);cout<<b.m_public<<endl; //错误,无法直接调用私有成员
}
2. 类的四个默认函数:构造,拷贝构造,赋值,析构(这一点要背下来,面试直接被问了)
  • 每当构建一个新的类,编译器都会给每个类生成以上四个默认的无参构造函数,并且这四个函数都是默认public的。
class A
{
public:double m_a;
};
int main( void )
{A a; //调用默认无参构造函数,此时m_a的值是不确定的,不能用//离开主函数前调用析构函数,释放a的内存
}

但是一旦程序员自己定了带参数的构造函数,那么编译器就不会再生成默认的无参构造函数了,但是还是有默认的拷贝和赋值构造函数。因此假如只定义了有参数的构造函数,那么这个类就没有无参构造函数。

class A
{
public:A(int i):m_a(i){}int m_a;
};
int main( void )
{A a; //错误!没有无参构造函数A a1(5); // 调用了A中程序员定义的有参构造函数A a2(6); // 调用了A中程序员定义的有参构造函数A a3 = a1; //此处调用默认的拷贝构造函数a2 = a1; //此处调用默认的赋值函数
}

上面的程序中尤其需要注意的是 A a3 = a1这一句,虽然有等号,但是仍然是拷贝构造函数。拷贝构造函数和赋值函数的区别在于等式左边的对象是否已经存在。a2 = a1这一句执行的时候,a2已经存在,因此是赋值函数,而执行A a3 = a1这一句的时候,a3还不存在,因此为拷贝构造函数。

默认的赋值和拷贝构造函数一般只是简单的拷贝类中成员的值,这一点当类中存在指针成员和静态成员变量的时候就非常危险。例如以下一种情况:

class A
{
public:A(int i, int* p):m_a(i), m_ptr(p){}int m_a;int *m_ptr;
};
int main( void )
{int m = 10, *p = &m;A a1(3, p);A a2 = a1; //a2 和 a1的m_ptr都指向了同一地址*p = 100;cout<<*(a2.m_ptr)<<endl; //输出为100
}

这也就是C++中由于指针带来的浅拷贝的问题,只赋值了地址,而没有新建对象。因此假如类中存在静态变量或者指针成员变量时一定要自己手动定义赋值、拷贝构造、析构函数。

class A
{
public:A(int b):m_public(b),m_protected(1), m_private(2){}int m_public;
protected:int m_protected;
private:int m_private;
};
int main( void )
{A a(10);cout<<"a.m_public"<<endl; //正确,可以直接调用cout<<a.m_protected<<endl; //错误,不可以直接调用cout<<a.m_private<<endl;//错误,不可能直接调用
}
  • 子类会继承父类定义的构造函数吗?

可以理解成不能。子类会继承父类所有的函数,包括构造函数**,但是子类的构造函数会把父类的构造函数覆盖了,所以看起来就是没有继承。假如子类不定义任何构造函数,那么子类只会默认地调用父类的无参构造函数。当父类中只定义了有参构造函数,从而不存在无参构造函数的话,子类就无法创建对象。**

class A
{
public:A(int b):m_public(b){}int m_public;
};
class B:public A
{
};int main()
{B b; //出错,因为父类没有无参构造函数
}

因此在这种情况必须要显示定义子类的构造函数,并且在子类构造函数中显示调用父类的构造函数。

class A
{
public:A(int b):m_public(b){}int m_public;
};
class B:public A
{
public:B(int num):A(num){}
};int main()
{B b1; //出错,由于父类没有无参构造函数,因此B也不存在无参构造B b2(5); //正确
}
  • 构造函数的构造顺序:先构造基类,再构造子类中的成员,再构造子类
class A
{
public:A(){cout<<"constructing A"<<endl;}
};class B
{
public:B(){cout<<"constructing B"<<endl;}
};class C:public A
{
public:B b;A a;int num;C(int n):num(n){cout<<"constructing C"<<endl;}};int main()
{C c(1);
}

运行结果为:

图片

第一行的constructingA就是在构建基类,然后构建b对象,再构建a对象,最后构建c本身。

而析构的顺序就正好是完全反过来,先析构子类,再析构子类中的对象,最后析构基类。

  • 假如把构造函数和析构函数定义成私有的会怎样?(被问的时候真的一脸懵,-_-//)

假如把构造函数定义为私有,那么类就无法直接实例化(还是可以实例化的,只是要转个弯)。来看下面这个例子:

class A
{
public:int m_public;static A* getInstance(int num){return new A(num);}
private:A(int b):m_public(b){}};int main()
{A a1(4); //错误A* pa = A::getInstance(5); //正确
}

有些时候,我们不希望一个类被过多地被实例化,比如有关全局的类、路由类等。这时候,我们就可以用这种方法为类设置构造函数并提供静态方法。

假如把类的析构函数定义为私有,那么就无法在栈中生成对象,而必须要通过new来在堆中生成对象。

另外在这里提及一点,对应的,如何让类只能在栈中生成,而不能new呢?就是将new 和delete重载为私有。

原因是C++是一个静态绑定的语言。在编译过程中,所有的非虚函数调用都必须分析完成。即使是虚函数,也需检查可访问性。因些,当在栈上生成对象时,对象会自动析构,也就说析构函数必须可以访问。而堆上生成对象,由于析构时机由程序员控制,所以不一定需要析构函数。

class A
{
public:int m_public;static A* getInstance(int num){return new A(num);}A(int b):m_public(b){}
private:~A(){}};int main()
{A a(5); //错误,因为系统无法自动调用析构函数A *p_a = new A(5); //正确,此时p_a指向的是堆中的内存
}
  • 构造函数的初始化列表(就是构造函数冒号后面的东西,叫初始化列表,需要与{}中的函数内容区分开)

有几种情况必须要使用初始化列表:常量成员、引用类型、没有默认构造函数的类类型

class A
{
public:int m_a;A(int num):m_a(num){}
};class B
{
public:const int m_const;int &m_ref;A a;B(int num, int b):m_ref(b), m_const(1), a(num) //初始化列表{cout<<"constructing B"<<endl;}
};int main()
{int n = 5;B b(1, n);
}

还需要注意的一点是,初始化列表里的真正赋值的顺序其实是按照成员变量的声明顺序,而不是初始化列表中显示的顺序。例如这里是先初始化m_const,然后是m_ref,最后是a。

  • 隐式转换与explicit关键字的使用

来看下面一个例子:

class A
{
public:int m_a;A(int num):m_a(num){cout<<"constructing A"<<endl;}
};void test(A a)
{cout<<a.m_a+1<<endl;
}
int main()
{A a1= 6; //此时等式右边的6隐式生成了一个以6为参数的A类对象test(6); //输出7,入参也隐式生成了一个以6为参数的A类对象cout<<a1.m_a<<endl; //输出6
}

从下面的运行结果可以看出,A的构造函数被调用了两次。

图片

这种隐式转换有时候神不知鬼不觉,为了避免这种情况,于是有了explicit关键字。当构造函数被声明为explicit后,就必须要显式调用构造方法来生成对象了,而无法进行隐式转换。

class A{
public:  int m_a;  explicit A(int num):m_a(num){cout<<"constructing A"<<endl;}
};void test(A a) { cout<<a.m_a+1<<endl;
}int main(){  A a1= 6; //错误  test(6); //错误
}
3. 虚函数

虚函数是C++实现多态的方法。虚函数和普通函数没有什么区别,只有当用基类指针调用子类对象的方法时才能够真正发挥它的作用,也只有在这种情况下,才能真正体现出C++面对对象编程的多态性质。

先来了解一下绑定的概念。函数体与函数调用关联起来叫做绑定。

  • 早绑定:早绑定发送在程序运行之前,也是编译和链接阶段。
class A
{
public:int m_a;A(int num):m_a(num){}int add(int n){return m_a + n;}
};int main()
{A a(1);cout<<a.add(5);
}

在上面的代码中,函数add在编译期间就已经确定了实现。这就是早绑定。所有的非虚函数都是早绑定。

  • 晚绑定:晚绑定发生在程序运行期间,主要体现在继承的多态方面。

引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚邦定,它就不是多态。”

class A
{
public:int m_a;A(int num):m_a(num){}void virtual show(){cout<<"base function"<<endl;}
};class B:public A
{
public:B(int n):A(n){}void show(){cout<<"derived function"<<endl;}
};
int main()
{A *pa = new B(1);pa->show(); //输出 derived function
}

父类和子类都定义了show方法,在继承的过程中,由于父类show方法是虚函数,而父类指针指向的是子类对象,所以会在子类对象中去找show函数的实现。假如子类中没有show,那么就还是会调用父类的show。
这个晚绑定的过程是通过虚指针实现的。只要一个类中声明了有虚函数,那么编译器就会自动生成一个虚函数表。虽然名字叫表,但本质是一个存放虚函数指针的函数指针数组。一个虚表对应一个指针。当该类作为基类,其派生类对基类的(一个或者多个)虚函数进行重写时,派生类的虚函数表中,相应的函数指针的值就会发生变化。

构造函数不能声明为虚函数。虚函数是晚绑定,一定是先有了基类对象,才会有对应的虚指针,再去本类或者子类对象中去找对应的实现。所以一定要先通过构造函数创建了对象,才能去实现虚函数的作用。

而析构函数,则常常被声明为虚函数。(记得当时面试官问我,析构函数能不能是虚的,我当时斩钉截铁得回答,不能!

先看下面这个例子。

class A
{
public:int m_a;A(int num):m_a(num){cout<<"constructing A"<<endl;}~A(){cout<<"destructing A"<<endl;}
};class B:public A
{
public:B(int n):A(n){cout<<"constructing B"<<endl;}~B(){cout<<"destructing B"<<endl;}
};
int main()
{A* pa= new B(1);delete pa; 
}

执行结果为:

图片

可以看到是先构建了A对象,然后构建了B对象。可是却只析构了A对象,B对象的内存空间就泄漏了。

现在把A的析构函数置为虚函数的话,

class A
{
public:int m_a;A(int num):m_a(num){cout<<"constructing A"<<endl;}virtual ~A(){cout<<"destructing A"<<endl;}
};class B:public A
{
public:B(int n):A(n){cout<<"constructing B"<<endl;}~B(){cout<<"destructing B"<<endl;}
};
int main()
{A* pa= new B(1);delete pa;
}

运行结果为:

图片

可以看到这时B对象也被析构了。这正是利用了虚函数晚绑定的特点,当调用基类指针析构函数的时候,先调用B的析构函数,再调用A的析构函数。

对于虚函数始终要注意只有用指针调用的时候才会有作用,假如只是普通的对象调用,虚函数是不起作用的。

class A
{
public:int m_a;A(int num):m_a(num){}virtual void show(){cout<<"A::show()"<<endl;}virtual ~A(){}
};class B:public A
{
public:B(int n):A(n){}void show(){cout<<"B::show()"<<endl;}~B(){}
};
int main()
{A a(1); a.show(); //输出A::show()B b(1); b.show(); //输出B::show()
}
4. 成员函数的重载、隐藏与覆盖
  • 成员函数的重载

(1)相同的范围(在同一个类中)
(2)函数名字相同
(3)参数不同 ,也可以仅仅是顺序不同
(4)virtual 关键字可有可无

class A
{
public:int m_a;A(int num):m_a(num){}void show(int n){}                    // (1)virtual void show(int n){}            //  (2) 错误!!不是重载,重复定义了(1),因为virtual关键字不能重载函数void show(double d){}                 // (3)show函数的重载void show(int a, double b){}          // (4)show函数的重载void show(double b, int a){}          //  (5)show函数的重载void show(int a, double b) const {}   //  (6)show函数的重载,const关键可以作为重载的依据void show(const int a, double b){}    // (7)错误!!不是重载, 顶层const不可以作为重载的依据,重复定义了(6)void show(int *a){}                   //  (8)show函数的重载void show(const int *a){}             // (9)show函数的重载,只有底层const才可以作为重载的依据void show(int * const a){}            //  (10) 错误!!不是重载,重复定义了(8),因为这里也使用了顶层const};

至于const能不能成为重载的依据取决于是顶层const还是底层const。顶层const是指对象本身是常量,而底层const是指指向或引用的对象才是常量。 底层const可以作为重载的依据,顶层const不可以。

  • 成员函数的隐藏,这里“隐藏”是指派生类的函数屏蔽了与其同名的基类函数

只要子类函数的名字与基类的相同,那么不管参数相同与否,都会直接屏蔽基类的同名函数。

class A
{
public:void show(int a) {cout<<"A::show()"<<endl;}//(1)
};
class B:public A
{
public:void show(){cout<<"B::show()"<<endl;} //(2)将(1)屏蔽了
};
  • 假如在子类中仍旧需要用到基类的同名函数,就要用using关键字显式声明。
class A
{
public:void show(int) {cout<<"A::show()"<<endl;}
};
class B:public A
{
public:using A::show;void show(){show(0); //一定要在前面显式声明using A中的show函数,否则此句会编译错误cout<<"B::show()"<<endl;}
};int main()
{B b;b.show();
}

输出结果为:

图片

  • 成员函数的覆盖

(1)不同的范围(分别位于派生类与基类)
(2)函数名字相同
(3)参数相同
(4) 基类函数必须有virtual 关键字

这其实就是多态的实现过程。注意参数必须要完全一致。之前讲过,在此不再赘述。

5. inline关键字

定义在类中的成员函数默认都是内联的。内联函数和普通函数的区别在于:当编译器处理调用内联函数的语句时,不会将该语句编译成函数调用的指令,而是直接将整个函数体的代码插人调用语句处,就像整个函数体在调用处被重写了一遍一样。这有助于提高程序的运行效率。但是要注意inline函数仅仅是一个对编译器的建议,所以最后能否真正内联,看编译器的意思,它如果认为函数不复杂,能在调用点展开,就会真正内联,并不是说声明了内联就会内联,声明内联只是一个建议而已。

**这也是为什么虚函数可以是内连函数。**因为仅仅是建议,当虚函数需要表现出多态性质的时候,编译器会选择不内连。

6. 友元函数、友元类

类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。

class A
{
public:A(int n):m_a(n){}friend class B; //声明B为A的友元类
private:int m_a;
};class B
{
public:B(A a){cout<<a.m_a<<endl;} //由于B是A的友元类,所以可以直接调用a的私有m_a成员
};int main()
{A a(1);B b = B(a); //输出1
}

要注意尽管友元类很强大,但是友元类和类本身并没有任何继承关系和成员关系。友元类或友元函数都不是本类的成员类和成员函数。

就如名字定义的那样,只是朋友,不具有任何亲属关系,因此无法使用this指针进行调用

友元函数常用在重载运算符。因为通常重载运算符的时候都要用到私有变量,所以用友元函数来重载是非常合适的。

7. 运算符的重载
  • 首先要明确,有6个运算符是不可以被重载的。

. (成员访问运算符)

.*, ->* (成员指针访问运算符)

:: (域运算符)

sizeof (长度运算符)

?: (条件运算符)

  • =, [] ,() ,-> 四个符号只能通过成员函数来重载,不能通过友元函数来定义

=,->, [], () 为什么不能重载为友元函数,是因为当编译器发现当类中没有定义这4个运算符的重载成员函数时,**就会自己加入默认的运算符重载成员函数。**而如果这四个运算符写成友元函数时会报错,产生矛盾。

  • 不允许用户定义新的运算符作为重载运算符,不能修改原来运算符的优先级和结合性,不能改变操作对象等等限制

  • 重载原则如下:

如果是一元操作,就用成员函数去实现

如果是二元操作,就尽量用友元函数去实现

如果是二元操作,但是对两个操作对象的处理不同,那么就尽可能用成员函数去实现

  • 运算符的重载
class A
{
public:A(int n):m_a(n){}int m_a;friend A operator+(A const& a1, A const & a2);
};A operator+(A const& a1, A const & a2)
{A res(0);res.m_a = 1 + a1.m_a + a2.m_a;return res;
}int main()
{A a1(1), a2(2);A a3 = a1 + a2;cout<<a3.m_a; //输出4
}
8. 再谈const关键字
  • 常量指针指向常对象, 常对象只能调用其常成员函数
class A
{
public:A(int n):m_a(n){}int m_a;void show(){cout<<"A::show()"<<endl;}};int main()
{A a1(1);const A a2(2);a1.show(); //正确a2.show(); //错误
}

假如增加const函数后,就可以正常运行。

class A
{
public:A(int n):m_a(n){}int m_a;void show(){cout<<"A::show()"<<endl;}void show() const{cout<<"A::show() const"<<endl;}};int main()
{A a1(1);const A a2(2);a1.show(); //正确 输出A::show()a2.show(); //正确 输出A::show() const,自动调用const函数
}
  • A const* 和const A*等价,允许用A* 赋值A const*,但是不允许用A const* 赋值A*
class A
{
public:A(int n):m_a(n){}int m_a;void changeValue(int *p){cout<<"changeValue"<<endl;}void changeValue2(int const *p){cout<<"changeValue2"<<endl;}};int main()
{int n = 10;int const *p_n_const = &n;int *p_n = &n;A a(1);a.changeValue(p_n_const); //错误,无法把int const*类型,转成int *类型,但是反之可以a.changeValue2(p_n); //正确,输出changeValue2
}

:
A(int n):m_a(n){}
int m_a;
void show(){cout<<“A::show()”<<endl;}
void show() const{cout<<“A::show() const”<<endl;}

};

int main()
{
A a1(1);
const A a2(2);
a1.show(); //正确 输出A::show()
a2.show(); //正确 输出A::show() const,自动调用const函数
}


-   **A const\* 和const A\*等价,允许用A\* 赋值A const\*,但是不允许用A const\* 赋值A\***```c++
class A
{
public:A(int n):m_a(n){}int m_a;void changeValue(int *p){cout<<"changeValue"<<endl;}void changeValue2(int const *p){cout<<"changeValue2"<<endl;}};int main()
{int n = 10;int const *p_n_const = &n;int *p_n = &n;A a(1);a.changeValue(p_n_const); //错误,无法把int const*类型,转成int *类型,但是反之可以a.changeValue2(p_n); //正确,输出changeValue2
}

这是因为形参const A表示指向的对象不能改变,所以如果传入A实参,只要不改变对象的值就不会有问题。但是如果形参为A*,则有可能改变A指向的对象,这是const A办不到的,所以编译器不允许传入const A*作为实参传入。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/750987.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[WUSTCTF2020]颜值成绩查询 --不会编程的崽

这题也是一个很简单的盲注题目&#xff0c;这几天sql与模板注入做麻了&#xff0c;也是轻松拿捏。 它已经提示&#xff0c;enter number&#xff0c;所有猜测这里后台代码并没有使用 " 闭合。没有明显的waf提示&#xff0c; 但是or&#xff0c;and都没反应。再去fuzz一…

二叉树OJ练习

本文旨在讲解有关二叉树的OJ题目&#xff0c;希望读完本文&#xff0c;能让读者都二叉树有更深一步的认识&#xff01; 正文开始&#xff01; 106. 根据二叉树创建字符串 算法思想&#xff1a; 根据题目的输出结果&#xff0c;可以观察出如下规律&#xff01; 1.若左右结点都…

Spring Boot整合STOMP实现实时通信

目录 引言 代码实现 配置类WebSocketMessageBrokerConfig DTO 工具类 Controller common.html stomp-broadcast.html 运行效果 完整代码地址 引言 STOMP&#xff08;Simple Text Oriented Messaging Protocol&#xff09;作为一种简单文本导向的消息传递协议&#xf…

sqllab第二十七A关通关笔记

知识点&#xff1a; 双引号闭合union select 大小写绕过 Union Select这里不能进行错误注入&#xff0c;无回显 经过测试发现这是一个双引号闭合 构造payload:id1"%09and%091"1 页面成功回显 构造payload:id0"%09uNion%09SElect%091,2,3%09"1 页面成功…

在雄安新区买新房要注意什么?有哪些注意事项?

雄安新区新建住宅均价每平方米11735元起&#xff0c;二手房每平方米8950元起。 整体价格非常有优势。 雄安新区房价走势与区域发展直接相关。 而且&#xff0c;雄安新区已经成立五周年了。 2022年&#xff0c;雄安新区多项重点项目将陆续竣工。 雄安新区城市基础设施建设已初具…

Linux之shell循环

华子目录 for循环带列表的for循环格式分析示例shell允许用户指定for语句的步长&#xff0c;格式如下示例 不带列表的for循环示例 基于C语言风格的for循环格式示例注意 while循环格式示例 until循环作用格式示例 循环控制breakcontinue详细语法示例 循环嵌套示例 for循环 for循…

深度学习——SAM(Segment-Anything)代码详解

目录 引言代码目录segment-anything 代码详解build_sam.pypredictor.pyautomatic_mask_generator.py 引言 从去年年初至今&#xff0c;SAM(Segment Anything )已经问世快一年了&#xff0c;SAM凭借其强大而突出的泛化性能在各项任务上取得了优异的表现&#xff0c;广大的研究者…

源码编译部署LAMP

编译部署LAMP 配置apache [rootzyq ~]#: wget https://downloads.apache.org/apr/apr-1.7.4.tar.gz --2023-12-11 14:35:57-- https://downloads.apache.org/apr/apr-1.7.4.tar.gz Resolving downloads.apache.org (downloads.apache.org)... 88.99.95.219, 135.181.214.104…

BUUCTF-WEB1

[ACTF2020 新生赛]Exec1 1.打开靶机 是一个ping命令 2.利用管道符“|” ping一下本地主机并查看ls ping 127.0.0.1 | ls 可以看到回显的内容是一个文件 127.0.0.1 | cat index.php #查看主机下index.php 127.0.0.1 | ls / #查看主机根目录下的文件 看的一个flag文件 …

数据仓库数据分层详解

数据仓库中的数据分层是一种重要的数据组织方式&#xff0c;其目的是为了在管理数据时能够对数据有一个更加清晰的掌控。以下是数据仓库中的数据分层详解&#xff1a; 原始数据层&#xff08;Raw Data Layer&#xff09;&#xff1a;这是数仓中最底层的层级&#xff0c;用于存…

jupyter闪退和自动跳转问题

1.闪退问题 当我们点击jupyter时&#xff0c;它会闪一下&#xff0c;然后无法进入&#xff0c;这个时候我们可以去prompt命令行输入jupyter notebook启动试试&#xff0c;如果还不行&#xff0c;我们可以根据报错去解决&#xff0c;一般csdn上都有对应情况&#xff0c;直接搜索…

Linux-新手小白速秒Hadoop集群全生态搭建(图文混编超详细)

在之前的文章中&#xff0c;我教会大家如何一步一步搭建一个Hadoop集群&#xff0c;但是只提供了代码&#xff0c;怕有些朋友会在一些地方产生疑惑&#xff0c;今天我来以图文混排的方式&#xff0c;一站式交给大家如何搭建一个Hadoop高可用集群包括&#xff08;HadoopHA&#…

el-select使用filterable下拉无法关闭得问题

这里推荐一个前端框架 sakuya / SCUI&#xff0c;他里面有个formTable&#xff0c;可以解决很多订单明细保存得问题。基本沿用element-plus的前端使用模式&#xff0c;让表单表格变的非常容易。 这个的供应商插件&#xff0c;当使用filterable后&#xff0c;点击表格重的选项&…

Redis Desktop Manager:一站式Redis数据库管理与优化

Redis Desktop Manager是一款功能强大的Redis桌面管理工具&#xff0c;也被称作Redis可视化工具。以下是其主要的功能特色&#xff1a; 连接管理&#xff1a;Redis Desktop Manager支持连接多个Redis服务器&#xff0c;用户可以在同一界面下管理多个数据库&#xff0c;大大提高…

记录一下在Pycharm中虚拟环境的创建

如果在Pycharm中要新建一个虚拟环境&#xff0c;那你可以在Terminal中选择Command Prompt&#xff0c;在这里面执行相关命令 一、安装了Anaconda&#xff0c;创建虚拟环境 当你使用解释器是Anaconda提供的时&#xff0c;你可以使用conda命令执行&#xff0c;见以下操作&#x…

前端Vue与uni-app中的九宫格、十二宫格和十五宫格菜单组件实现

在前端 Vue 开发中&#xff0c;我们经常会遇到需要开发九宫格、十二宫格和十五宫格菜单按钮的需求。这些菜单按钮通常用于展示不同的内容或功能&#xff0c;提供给用户快速访问和选择。 一、引言 在前端开发中&#xff0c;九宫格、十二宫格和十五宫格菜单按钮是一种常见的布局…

202206 CSP认证 | 角色授权

角色授权 fine&#xff0c;又是一道acwing上TLE但是平台通过了的&#xff0c;那就酱吧… 直接跟着题目来模拟的…先找到每个用户授予的所有角色&#xff0c;包括用户本身和它所属的用户组。 然后遍历这个角色集合&#xff0c;看是否有操作权限&#xff0c;种类权限以及资源名称…

SVN修改已提交版本的注释

目录 一、需求分析 二、问题分析 三、解决办法 一、需求分析 ​开发过程中&#xff0c;在SVN提交文件后&#xff0c;发现注释写的不完整或不够明确&#xff0c;想再修改之前的注释文字​。 使用环境&#xff1a; SVN服务器操作系统&#xff1a;Ubuntu 20.04.6 LTS SVN版本&…

JVM实战篇

内存调优 内存溢出和内存泄漏 内存泄漏&#xff1a;在java中如果不再使用一个对象&#xff0c;但是该对象依然在GC ROOT的引用链上&#xff0c;这个对象就不会被垃圾回收器回收。 内存泄漏绝大多数情况都是由堆内存泄漏引起的&#xff0c;所以后续没有特别说明则讨论的都是堆…

Linux-centos如何搭建yum源仓库

1.本地搭建&#xff08;无需连接外网&#xff09; 1.1检查网络配置&#xff0c;及网络连接 打开虚拟机&#xff0c;点击【编辑——虚拟网络编辑器】 点击【仅主机模式】查看子网段是否和局内IP匹配 进入局内&#xff0c;查看网络IP是否在你上述设置的网段内&#xff0c;如果不…