Introduction to Programming in C++
string_view. Resources.
Georgii Zhulikov
Apr 17, 2023
Lecture #27
std::string and C-strings
C-strings
Read-only access means reusing a const pointer.
Read-only substrings can be defined as a pointer and size pair (manually handled).
std::string
Read-only access through const references.
Read-only substrings lead to copies.
2
Read-only substrings
Many string-processing tasks only require reading the strings and computing statistics, but not modifying anything.�Many modifications can be made in advance.
Result: a large buffer of text which is split, analyzed, counted, but not modified.
What if we combine these two approaches?
3
std::string textData = getTextData(filePath);
// make a list of all words
std::vector<std::string> words = extractWords(textData);
// make a list of all sentences
std::vector<std::string> sentences = extractSentences(textData);
// make a map word-sentence
// "in which sentences does this word appear?"
using WordMap = std::map<std::string, std::string>;
WordMap wordMap = makeWordSentenceMap(words, sentences);
// do complex processing with the map
Why const std::string& doesn’t solve the problem
Passing objects by reference is supposed to solve exactly this problem - unnecessary copies.
However, there are still:
copy
no copy (reference)
4
std::string getLongestWord(std::string sentence)
{
...
}
std::string getLongestWord(
const std::string& sentence)
{
...
}
Why const std::string& doesn’t solve the problem
The steps happening here are:
5
std::string word;
word = getLongestWord("A small sentence");
std::string getLongestWord(
const std::string& sentence)
{
...
}
const char* data = "A small sentence";
std::string word;
word = getLongestWord(data);
Read access to a single buffer
C-strings can solve this without unnecessary copying, but they need to store the size.
6
"A very long string."
std::pair<const char*, size_t> getLongestWord(
const char* sentence, size_t size)
{
}
string_view
std::string_view provides exactly this solution.
In addition, it provides an interface with read-only operations find, substr, and so on.
7
"A very long string"
std::string_view
ptr = str
size = 18
std::string_view
ptr = str + 12
size = 6
string_view
8
std::string_view getLongestWord(std::string_view sentence)
{
...
}
string_view
The steps happening here are:
9
std::string word;
word = getLongestWord("A small sentence");
std::string_view getLongestWord(
std::string_view sentence)
{
...
}
const char* data = "A small sentence";
std::string word;
word = getLongestWord(data);
Resources and Owners
10
std::string
String data
std::vector
Vector elements
std::string_view
pointer
Resources and Owners
Manually managed data must be treated carefully.
STL containers solve this by providing non-pointer interface.
11
Data* createData()
{
Data* data = new Data();
initialize(data);
preprocess(data);
//...
return data;
}
Data* demoData = createData();
ModificationStatus status = modifyData(demoData);
AnalysisResult analysis = analyzeData(demoData);
DATA
data
demoData
modifyData() input
analyzeData() input
Resource Management with std::string_view
std::string_view doesn’t keep track of the original object except by its pointer. It is susceptible to the point:
Solution? Keep track of this manually.
Example on the right is wrong. Why?
12
std::string_view getFirstWord(std::string text)
{
int endIndex = text.size();
for (int i = 0; i < text.size(); ++i)
{
if (text[i] == ' ')
{
endIndex = i;
}
}
std::string_view word = text.substr(0, endIndex);
return word;
}
Resource Management with std::string_view
std::string is passed by copy, so the variable text only exists within the function.
std::string_view “attaches” to this temporary variable.
When the variable is destroyed (at the end of the function), std::string_view becomes attached to unused memory.
13
std::string_view getFirstWord(const std::string& text)
{
int endIndex = text.size();
for (int i = 0; i < text.size(); ++i)
{
if (text[i] == ' ')
{
endIndex = i;
}
}
std::string_view word = text.substr(0, endIndex);
return word;
}
std::string
String Data
std::string
String Data
std::string_view
std::string_view
Implementing string_view
An example how some basic parts of a string_view class might work.
14
size_t getSize() const { return _size; }
MyStringView getSubString(size_t pos, size_t size) const
{
MyStringView result(_ptr + pos, size);
return result;
}
char operator[](int index) const
{
return _ptr[index];
}
protected:
const char* _ptr;
size_t _size;
};
class MyStringView
{
public:
MyStringView(const std::string& orig)
: _ptr(orig.c_str()),
_size(orig.size()) {}
MyStringView(const char* ptr, size_t size)
: _ptr(ptr),
_size(size) {}
MyStringView(const MyStringView& orig)
: _ptr(orig._ptr),
_size(orig._size) {}
Resource Management with Smart Pointers
15
Resource Management with Smart Pointers
A simple example is using unique_ptr to automatically free memory in a function.
If a regular pointer is used, you must keep track of all ways the function may be terminated.
With unique_ptr memory is freed automatically.
16
void my_func()
{
int* valuePtr = new int(15);
int x = 45;
// ...
if (x == 45)
return; // here we have a memory leak,
// valuePtr is not deleted
// ...
delete valuePtr;
}
#include <memory>
void my_func()
{
std::unique_ptr<int> valuePtr(new int(15));
int x = 45;
// ...
if (x == 45)
return; // no memory leak anymore!
// ...
}
Qt and Smart Pointers
Instructions for downloading and setting up Qt will be released soon. Quick links:
17
Recap. Questions.
18
Next time
Qt
19