【c++ primer】第十二章 类和动态内存分配

本文介绍C++中静态成员的概念与应用,并通过实现一个简单的字符串类来演示静态成员变量、构造函数、复制构造函数及赋值操作的区别。同时讨论了如何避免浅复制带来的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一,概念

1)静态存储方式:指在程序运行期间分配固定的存储空间的方式。外部变量和静态变量属于此种存储方式

2)静态成员变量:为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见。无论创建多少对象,程序都只创建一个静态变量副本。

3)静态成员特点:静态成员存在于内存,非静态成员需要实例化才会分配内存,所以静态成员不能访问非静态的成员..因为静态成员存在于内存,所以非静态成员可以直接访问类中静态的成员。

4)不能在类声明中初始化静态成员变量,因为声明描述了如何分配内存,但并不分配内存。

5)delete/new new char[ ]/delete [ ] 成对出现

6)new 创建对象后,再用delete 删除对象可以释放对象本身占用的内存。但并不能自动释放属于对象成员的指针指向的内存。

#include <iostream> #include <cstring> using namespace std; class StringBad { private: char *str; int len; static int num_strings; public: StringBad(const char *s); StringBad(); ~StringBad(); friend ostream &operator<< (ostream &os,const StringBad &st); }; StringBad::StringBad(const char* s) { len=strlen(s); str=new char[len+1]; strcpy(str,s); num_strings++; cout<<num_strings<<":\""<<str<<"\""<<"object created\n"; } StringBad::StringBad() { len=4; str=new char[4]; strcpy(str,"C++"); num_strings++; cout<<num_strings<<":\""<<str<<"\""<<"object created\n"; } StringBad::~StringBad() { --num_strings; cout<<"\""<<str<<"\""<<"object deleted\n"; cout<<num_strings<<"left\n"; delete [] str; } ostream & operator<< (ostream &os,const StringBad &st)//must add const { os<<st.str; return os; } int StringBad::num_strings=0; void callme(StringBad &);//非成员函数 void callme(StringBad &rab) { cout<<"string passed by reference:"; cout<<"\""<<rab<<"\"\n"; } int main() { StringBad headline1("tianshuai"); StringBad headline2("is a good student"); StringBad sports("he do good job"); cout<<"headline1:"<<headline1<<endl; cout<<"headline2:"<<headline2<<endl; cout<<"sports:"<<sports<<endl; callme(headline1); return 0; }

输出:1:"tianshuai"object created 2:"is a good student"object created 3:"he do good job"object created headline1:tianshuai headline2:is a good student sports:he do good job string passed by reference:"tianshuai" "he do good job"object deleted 2left "is a good student"object deleted 1left "tianshuai"object deleted 0left 二,构造函数

1)默认构造函数:显示定义构造函数即无参数构造函数,如果有参数构造函数所有参数都有默认值

2)复制构造函数:当用户没有定义自已的复制构造函数时系统将生成一个默认的复制构造函数。当按值传递对象时,就会创建一个形参的临时对象,然后调用复制构造函数把临时对象的值复制给实参。

功能:将一个对象的非静态成员的值逐个复制给另一个对象,注意复制的是成员的值,这种复制方式也称为浅复制。因为静态成员属于整个类,而不属于某个对象,所以调用复制构造函数时静态成员不会受到影响。

何时使用:按值传递对象,函数返回对象,用一个对象初始化另一个对象即复制初始化时,根据元素初始化列表初始化数组元素。这四种情况都将调用复制构造函数。记住,复制构造函数只能用于初始化,不能用于赋值,赋值时不会调用复制构造函数,而是使用赋值操作符。

3)解析复制构造函数:s1=s2;

s1.str=s2.str;//这里s1复制的不是字符串而是指向字符串的指针

s1.len=s2.len;

当s2的析构函数被调用时,释放str指向的内存delete [ ]s2.str。则s1出现问题

解决办法:深度复制

StringBad::StringBad(const StringBad &st)

{

len=st.len;

str=new char[len +1 ]; //关键使用new 动态申请存放字符串的内存

strcpy(str,st.str)

}

4)理解赋值复制初始化的区别(重点):赋值是在两个已存在的对象间进行的,也就是用一个已存在的对象去改变另一个已存在对象的值。赋值将调用赋值操作符对对象进行操作,赋值操作符将在操作符重载中讲解。比如有类hyong,有语句hyong x(1);hyong y(1,2)则x=y;这就是赋值,因为对象x和y是已经存在的对象,而语句hyong x=y;则是复制初始化,是用一个已存在的对象y去创建一个新对象x,所以是复制初始化。

三,改进string

1)strcmp(const char *s1,const char * s2)

当s1<s2时,返回值<0   当s1=s2时,返回值=0   当s1>s2时,返回值>0

2)静态方法:静态方法与静态变量一样,属于本身,而不属于那个类的一个对象。要想调用一个被定义为static的方法,必须在它前面加上这个类的名称。

使用规则:静态方法只能访问类的静态成员,不能访问类的非静态成员;

   非静态方法可以访问类的静态成员,也可以访问类的非静态成员;
   静态方法不能使用实例来调用,只能使用类名来调用。

3)布局new操作符:在分配内存时 指定已存在的内存位置。在已知数组中再申请适当大小区域

char *buffer=new char[512];

p1 = new (buffer)JustTesting;

p2 = new (buffer + sizeof(JustTesting))JustTesting("tian",5);

delete [ ] buffer;不调用p1,p2析构函数

delete p1; 释放buffer而不是 p1

p1->~JustTesting();









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值