欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 创投人物 > C++基础精讲-07

C++基础精讲-07

2025/4/19 15:01:39 来源:https://blog.csdn.net/2401_83561886/article/details/147223205  浏览:    关键词:C++基础精讲-07

文章目录

  • 1. const对象
  • 2. 指向对象的指针
  • 3. 对象数组
  • 4. c++中const常见用法总结
    • 4.1 修饰常量
    • 4.2 修饰指针
    • 4.3 修饰函数参数
    • 4.4 修饰函数返回值
    • 4.5 修饰成员函数
    • 4.6 const对象
  • 5. 赋值运算符函数(补充)
    • 5.1 概念
    • 5.2 默认赋值运算符函数局限
    • 5.3 解决办法


1. const对象

1.概念:在 C++ 中,const 用于定义常量对象,一旦对象被初始化,其值就不能再改变。因为 const 对象只能被创建、撤销和只读访问,写操作是不允许的。
2.基本使用

#include <iostream>
#include <cstring>namespace myspace1
{using std::endl;using std::cout;using std::cin;using std::string;
}using namespace myspace1;class Person
{
public:Person() :_age(0), _name(new char[strlen("张三") + 1]){cout << "无参构造" << endl;strcpy(_name, "张三");}Person(int age,  const char* name) :_age(age), _name(new char[strlen(name) + 1]){cout << "有参构造" << endl;strcpy(_name, name);}void setAge(int age){_age = age;}void print(){cout << "age=" << _age << endl;cout << "name=" << _name << endl;}//const成员函数,不能修改对象的数据成员void print() const{cout << "age=" << _age << endl;cout << "name=" << _name << endl;}~Person(){delete[] _name;cout << "析构函数" << endl;}
private:int _age;char* _name;
};int main(void)
{//普通对象Person p1(1,"张大");p1.print();p1.setAge(10);p1.print();//const 对象只能被创建、撤销和只读访问,写操作是不允许的const Person p2(2, "张二");//p2.setAge(20)   //不允许//p2.print();     //不允许p2.print();        //调用的是void print() constreturn 0;
}

在这里插入图片描述

3.总结
const对象与const成员函数的规则:
(1)当类中有const成员函数和非const成员函数重载时,const对象会调用const成员函数,非const对象会调用非const成员函数;
(2)当类中只有一个const成员函数时,无论const对象还是非const对象都可以调用这个版本;
(3)当类中只有一个非const成员函数时,const对象就不能调用非const版本。

建议:如果一个成员函数中确定不会修改数据成员,就把它定义为const成员函数。

2. 指向对象的指针

int main(void)
{//在栈上开辟空间Person p1(1, "一号");Person* p2 = &p1;  //指向p1的指针;Person* p3 = nullptr;  //空指针;//在堆上开辟空间Person* p4 = new Person(2, "二号");delete p4;p4 = nullptr;Person* p5 = new Person();p5->setAge(20);  p5->print();(*p5).setAge(30);(*p5).print();delete p5;p5 = nullptr;return 0;
}

3. 对象数组

#include <iostream>
#include <cstring>namespace myspace1
{using std::endl;using std::cout;using std::cin;using std::string;
}using namespace myspace1;class Person
{
public:Person() :_age(0), _name(new char[strlen("张三") + 1]){cout << "无参构造" << endl;strcpy(_name, "张三");}Person(int age,  const char* name) :_age(age), _name(new char[strlen(name) + 1]){cout << "有参构造" << endl;strcpy(_name, name);}void setAge(int age){_age = age;}void print(){cout << "age=" << _age << endl;cout << "name=" << _name << endl;}//const成员函数,不能修改对象的数据成员void print() const{cout << "age=" << _age << endl;cout << "name=" << _name << endl;}~Person(){delete[] _name;cout << "析构函数" << endl;}
public:int _age;char* _name;
};int main(void)
{// 方式一:使用无参/默认构造函数创建对象数组cout << "使用默认构造函数创建对象数组:" << endl;Person p1[3];for (int i = 0; i < 3; i++) {p1[i].print();}// 方式二:使用有参构造函数初始化对象数组cout << "使用有参构造函数初始化对象数组:" << endl;Person p2[3] = {Person(20, "李四"),Person(21, "王五"),Person(22, "赵六")};for (int i = 0; i < 3; ++i) {p2[i].print();}// 方式三:动态分配对象数组cout << "动态分配对象数组:" << endl;Person* p3 = new Person[2];p3[0].setAge(25);strcpy(p3[0]._name, "孙七");p3[1].setAge(26);strcpy(p3[1]._name, "周八");for (int i = 0; i < 2; ++i) {p3[i].print();}// 释放动态分配的内存delete[] p3;return 0;
}

4. c++中const常见用法总结

4.1 修饰常量

//只有读权限,没有写权限
const int a = 10;    //==int const a=10;
//a = 20;  不可以修改

4.2 修饰指针

int main(void)
{int a = 10;int b = 20;//1.指向常量的指针const int* p1 = &a;  //==int const* p1 = &a;//*p1 = 100;   不可以为通过指针修改a的值;p1 = &b;  //可以修改指针p1的指向//2.常量指针;可以通过指针修改变量的值,但是指针的指向不可以变;int* const p2 = &a;*p2 = 20;//p2 = &b;  错误//3.指向常量的常量指针:指针的指向和所指向的值都不能改变const int* const p3 = &a;// *p3 = 20; // 错误,不能通过指针修改所指向的值// p3 = &b; // 错误,不能改变指针的指向return 0;
}

4.3 修饰函数参数

const 用于修饰函数参数时,能够保证在函数内部不会修改该参数的值。

void fun(const int a)
{//a = 100;   错误cout << "a=" <<a<< endl;
}int main(void)
{int a = 10;fun(a);return 0;
}

4.4 修饰函数返回值

const 修饰函数返回值时,表明返回值是一个常量,不能被修改。

//const 修饰函数返回值时,表明返回值是一个常量,不能被修改。
int const fun()
{int a = 10;return a;           
}int main(void)
{//fun() = 100;  错误,不可以修改return 0;
}

4.5 修饰成员函数

const 修饰类的成员函数时,表明该成员函数不会修改对象的状态

class MyClass {
private:int _value;
public:MyClass(int val) : _value(val) {}// 常量成员函数int getValue() const {// value = 20; // 错误,不能在常量成员函数中修改成员变量return _value;}
};

4.6 const对象

const 对象是指那些一经创建,其状态(即成员变量的值)就不能被修改的对象

class MyClass {
public:int _value;MyClass(int v) : _value(v) {}
};int main() {const MyClass obj(10);// obj.value = 20; // 错误,不能修改 const 对象的成员变量cout << "Value: " << obj._value << endl;return 0;
}

5. 赋值运算符函数(补充)

5.1 概念

Point pt1(1, 2), pt2(3, 4);
pt1 = pt2;//赋值操作

在执行 pt1 = pt2; 该语句时, pt1 与 pt2 都存在,所以不存在对象的构造,这要与 Point pt2 =pt1; 语句区分开,这是不同的。所以当 = 作用于对象时,需要调用的是赋值运算符函数
格式:

类名& operator=(const 类名 &)
class Point {
private:int _ix;int _iy;
public:Point(int x = 0, int y = 0) : _ix(x), _iy(y) {}//默认赋值运算符重载函数Point& operator=(const Point& rhs){_ix = rhs._ix;_iy = rhs._iy;return *this;}void print() const {cout << "(" << _ix << ", " << _iy << ")" <<endl;}
};int main() {Point p1(1, 2);Point p2(3, 4);p2 = p1;p2.print();return 0;
}

5.2 默认赋值运算符函数局限

(1)当类中包含指针数据成员且该指针指向堆上分配的内存时,默认的赋值运算符函数(编译器自动生成的)就无法满足需求,这可能会导致浅拷贝问题,进而引发内存泄漏或悬空指针等错误。
(2)默认的赋值运算符执行的是浅拷贝,也就是只复制指针的值,而不复制指针所指向的内存。这会造成两个对象的指针成员指向同一块内存,当其中一个对象被销毁时,它会释放这块内存,而另一个对象的指针就会变成悬空指针。另外,如果两个对象都尝试释放同一块内存,会导致重复释放,引发未定义行为。

class Person
{
public://构造函数Person(const int* age) :_age(new int(*age)){cout << "构造函数" << endl;}//打印函数void print()const{cout << "age=" << *_age << endl;}//析构函数~Person(){delete _age;cout << "析构函数" << endl;}
private:int* _age;
};int main(void)
{int age1 = 20;int age2 = 30;Person p1(&age1);p1.print();Person p2(&age2);p2.print();p2 = p1;return 0;
}

问题分析:
当执行 p2 = p1; 时,会调用编译器自动生成的默认赋值运算符。默认赋值运算符执行的是浅拷贝,即只复制指针的值,而不复制指针所指向的内存。这会导致 p1 和 p2 的 _age 指针指向同一块内存。由于浅拷贝,当 p2 对象被销毁时,会释放 _age 所指向的内存。此时,p1 的 _age 指针就会变成悬空指针。当 p1 对象被销毁时,再次释放同一块内存,会导致重复释放,引发未定义行为。

5.3 解决办法

为了解决浅拷贝的问题,需要自定义赋值运算符函数,实现深拷贝。深拷贝会为新对象分配新的内存,并将原对象的数据复制到新的内存中。

class Person
{
public://构造函数Person(const int* age) :_age(new int(*age)){cout << "构造函数" << endl;}//打印函数void print()const{cout << "age=" << *_age << endl;}//自定义赋值运算符函数Person& operator=(const Person& rhs){//自我赋值检查if (this != &rhs) {//释放当前_age空间;delete _age;_age = nullptr;_age = new int(*rhs._age);}return *this;}//析构函数~Person(){delete _age;cout << "析构函数" << endl;}
private:int* _age;
};int main(void)
{int age1 = 20;int age2 = 30;Person p1(&age1);p1.print();Person p2(&age2);p2.print();p2 = p1;return 0;
}

若你有自行定义某些特殊成员函数,编译器会为类自动生成以下 6 个默认函数:

  1. 默认构造函数
  2. 析构函数
  3. 拷贝构造函数
  4. 拷贝赋值运算符
  5. 移动构造函数(C++11 及以后)
  6. 移动赋值运算符(C++11 及以后)

注意拷贝构造函数、赋值运算符函数、析构函数,如果需要手动定义其中的一个,那么另外两个也需要手动定义。 三合成原则

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词